Skip to content

Commit

Permalink
Julia 1.0 update (JuliaFirstOrder#59)
Browse files Browse the repository at this point in the history
* first batch of edits

* updated docstring

* second batch of edits

* updated REQUIRE and travis

* removed additional line break

* added 0.7 switch

* removed more additional line break

* edited readme and docs

* switched to TSVD

* added related packages

* re-enabled tests on osx

* updated appveyor config

* performance improvement

* docs update

* allowing failures
  • Loading branch information
lostella authored Sep 4, 2018
1 parent 849b438 commit cf120a3
Show file tree
Hide file tree
Showing 98 changed files with 892 additions and 908 deletions.
13 changes: 6 additions & 7 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,16 @@ os:
- linux
- osx
julia:
- 0.6
- 0.7
- 1.0
- nightly
matrix:
allow_failures:
- julia: 1.0
- julia: nightly
notifications:
email: false
# script:
# - julia -e 'Pkg.clone(pwd()); Pkg.build("ProximalOperators"); Pkg.test("ProximalOperators"; coverage=true)'
after_success:
- julia -e 'cd(Pkg.dir("ProximalOperators")); Pkg.add("Coverage"); using Coverage; Coveralls.submit(process_folder())'
- julia -e 'cd(Pkg.dir("ProximalOperators")); Pkg.add("Coverage"); using Coverage; Codecov.submit(Codecov.process_folder())'
- julia -e 'Pkg.add("Documenter")'
- julia -e 'cd(Pkg.dir("ProximalOperators")); include(joinpath("docs", "make.jl"))'
- julia -e 'using Pkg; cd(Pkg.dir("ProximalOperators")); Pkg.add("Coverage"); using Coverage; Coveralls.submit(process_folder())'
- julia -e 'using Pkg; cd(Pkg.dir("ProximalOperators")); Pkg.add("Coverage"); using Coverage; Codecov.submit(Codecov.process_folder())'
- julia -e 'using Pkg; Pkg.add("Documenter"); cd(Pkg.dir("ProximalOperators")); include(joinpath("docs", "make.jl"))'
12 changes: 8 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,12 @@ See the [documentation](https://kul-forbes.github.io/ProximalOperators.jl/latest

## Installation

To install the package, use the following in the Julia command line
To install the package, hit `]` from the Julia command line to enter the package manager, then

```julia
Pkg.add("ProximalOperators")
pkg> add ProximalOperators
```

Remember to `Pkg.update()` to keep the package up to date.

## Usage

With `using ProximalOperators` the package exports the `prox` and `prox!` methods to evaluate the proximal mapping of several functions.
Expand Down Expand Up @@ -63,6 +61,12 @@ and only returns the function value at the proximal point:
julia> fy = prox!(y, f, x, 0.5) # in-place equivalent to y, fy = prox(f, x, 0.5)
```

## Related packages

* [FirstOrderSolvers.jl](https://github.com/mfalt/FirstOrderSolvers.jl)
* [ProximalAlgorithms.jl](https://github.com/kul-forbes/ProximalAlgorithms.jl)
* [StructuredOptimization.jl](https://github.com/kul-forbes/StructuredOptimization.jl)

## References

1. N. Parikh and S. Boyd (2014), [*Proximal Algorithms*](http://dx.doi.org/10.1561/2400000003),
Expand Down
3 changes: 2 additions & 1 deletion REQUIRE
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
julia 0.6
julia 0.7
IterativeSolvers 0.4.0
TSVD 0.3.0
OSQP 0.1.4
37 changes: 23 additions & 14 deletions appveyor.yml
Original file line number Diff line number Diff line change
@@ -1,7 +1,17 @@
environment:
matrix:
- JULIA_URL: "https://julialang-s3.julialang.org/bin/winnt/x86/0.6/julia-0.6-latest-win32.exe"
- JULIA_URL: "https://julialang-s3.julialang.org/bin/winnt/x64/0.6/julia-0.6-latest-win64.exe"
- julia_version: 0.7
- julia_version: 1
- julia_version: nightly

platform:
- x86 # 32-bit
- x64 # 64-bit

matrix:
allow_failures:
- julia_version: 1
- julia_version: nightly

branches:
only:
Expand All @@ -15,19 +25,18 @@ notifications:
on_build_status_changed: false

install:
- ps: "[System.Net.ServicePointManager]::SecurityProtocol = [System.Net.SecurityProtocolType]::Tls12"
# Download most recent Julia Windows binary
- ps: (new-object net.webclient).DownloadFile(
$env:JULIA_URL,
"C:\projects\julia-binary.exe")
# Run installer silently, output to C:\projects\julia
- C:\projects\julia-binary.exe /S /D=C:\projects\julia
- ps: iex ((new-object net.webclient).DownloadString("https://raw.githubusercontent.com/JuliaCI/Appveyor.jl/version-1/bin/install.ps1"))

build_script:
# Need to convert from shallow to complete for Pkg.clone to work
- IF EXIST .git\shallow (git fetch --unshallow)
- C:\projects\julia\bin\julia -e "versioninfo();
Pkg.clone(pwd(), \"ProximalOperators\"); Pkg.build(\"ProximalOperators\")"
- echo "%JL_BUILD_SCRIPT%"
- C:\julia\bin\julia -e "%JL_BUILD_SCRIPT%"

test_script:
- C:\projects\julia\bin\julia --check-bounds=yes -e "Pkg.test(\"ProximalOperators\")"
- echo "%JL_TEST_SCRIPT%"
- C:\julia\bin\julia -e "%JL_TEST_SCRIPT%"

# # Uncomment to support code coverage upload. Should only be enabled for packages
# # which would have coverage gaps without running on Windows
# on_success:
# - echo "%JL_CODECOV_SCRIPT%"
# - C:\julia\bin\julia -e "%JL_CODECOV_SCRIPT%"
8 changes: 6 additions & 2 deletions demos/lasso.jl
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,12 @@
# minimize 0.5*||A*x - b||^2 + lam*||x||_1
#

using LinearAlgebra
using Random
using ProximalOperators

Random.seed!(0)

# Define solvers

function lasso_fista(A, b, lam, x; tol=1e-3, maxit=50000)
Expand All @@ -31,11 +35,11 @@ function lasso_fista(A, b, lam, x; tol=1e-3, maxit=50000)
end

function lasso_admm(A, b, lam, x; tol=1e-5, maxit=50000)
u = zeros(x)
u = zero(x)
z = copy(x)
f = LeastSquares(A, b)
g = NormL1(lam)
gam = 10.0/norm(A)^2
gam = 100.0/norm(A)^2
for it = 1:maxit
# perform f-update step
prox!(x, f, z - u, gam)
Expand Down
20 changes: 12 additions & 8 deletions demos/rpca.jl
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,13 @@
#
# See Parikh, Boyd "Proximal Algorithms", §7.2

using LinearAlgebra
using Random
using SparseArrays
using ProximalOperators

Random.seed!(0)

# Define solvers

function rpca_fista(A, lam1, lam2, S, L; tol=1e-3, maxit=50000)
Expand All @@ -28,8 +33,8 @@ function rpca_fista(A, lam1, lam2, S, L; tol=1e-3, maxit=50000)
# compute proximal (backward) step
prox!((S, L), g, (y_S, y_L), gam)
# stopping criterion
fix_point_res = max(vecnorm(S_extr-S, Inf), vecnorm(L_extr-L, Inf))/gam
rel_fix_point_res = fix_point_res/(1+max(vecnorm(S,Inf), vecnorm(L,Inf)))
fix_point_res = max(norm(S_extr-S, Inf), norm(L_extr-L, Inf))/gam
rel_fix_point_res = fix_point_res/(1+max(norm(S,Inf), norm(L,Inf)))
if rel_fix_point_res <= tol
break
end
Expand All @@ -46,19 +51,18 @@ L1 = randn(m, r)
L2 = randn(r, n)
L = L1*L2
S = sprand(m, n, p)
S = 20*S - 10.*S
V = sig*randn(m, n)
A = L + S + V
lam1 = 0.15*vecnorm(A, Inf)
lam2 = 0.15*norm(A)
lam1 = 0.15*norm(A, Inf)
lam2 = 0.15*opnorm(A)

# Call solvers

println("Calling solvers")

S_fista, L_fista = rpca_fista(A, lam1, lam2, zeros(m, n), zeros(m, n))
println("FISTA")
println(" nnz(S) = $(vecnorm(S_fista, 0))")
println(" nnz(S) = $(count(!isequal(0), S_fista))")
println(" rank(L) = $(rank(L_fista))")
println(" ||A|| = $(vecnorm(A, Inf))")
println(" ||A-S-L|| = $(vecnorm(A - S_fista - L_fista, Inf))")
println(" ||A|| = $(norm(A, Inf))")
println(" ||A-S-L|| = $(norm(A - S_fista - L_fista, Inf))")
4 changes: 2 additions & 2 deletions docs/src/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,10 @@ Please refer to the [GitHub repository](https://github.com/kul-forbes/ProximalOp

## Installation

To install the package, use the following in the Julia command line
To install the package, hit `]` from the Julia command line to enter the package manager, then

```julia
Pkg.add("ProximalOperators")
pkg> add ProximalOperators
```

To load the package simply type
Expand Down
2 changes: 1 addition & 1 deletion docs/src/operators.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ For ``k``-dimensional arrays, of size ``n_1 \times n_2 \times \ldots \times n_k`
```math
\langle A, B \rangle = \sum_{i_1,\ldots,i_k} A_{i_1,\ldots,i_k} \cdot B_{i_1,\ldots,i_k}
```
which reduces to the usual Euclidean product in case of unidimensional arrays, and to the *trace product* ``\langle A, B \rangle = \mathrm{tr}(A^\top B)`` in the case of matrices (bidimensional arrays). This inner product, and the associated norm, are the ones computed by `vecdot` and `vecnorm` in Julia.
which reduces to the usual Euclidean product in case of unidimensional arrays, and to the *trace product* ``\langle A, B \rangle = \mathrm{tr}(A^\top B)`` in the case of matrices (bidimensional arrays). This inner product, and the associated norm, are again the ones computed by `dot` and `norm` in Julia.

## Multiple variable blocks

Expand Down
44 changes: 25 additions & 19 deletions src/ProximalOperators.jl
Original file line number Diff line number Diff line change
Expand Up @@ -4,27 +4,35 @@ __precompile__()

module ProximalOperators

using IterativeSolvers
using OSQP
using LinearAlgebra

const RealOrComplex{T <: Real} = Union{T, Complex{T}}
const RealOrComplex{R <: Real} = Union{R, Complex{R}}
const HermOrSym{T, S} = Union{Hermitian{T, S}, Symmetric{T, S}}
const ArrayOrTuple{R} = Union{
AbstractArray{C, N} where {C <: RealOrComplex{R}, N},
Tuple{Vararg{AbstractArray{C, N} where {C <: RealOrComplex{R}, N}}}
}

export ProximableFunction
export prox, prox!
export gradient!

import Base: gradient
if VERSION > v"0.7"
export gradient
else
import Base: gradient
end

export gradient!

abstract type ProximableFunction end

# Utilities

include("utilities/deep.jl")
include("utilities/tuples.jl")
include("utilities/linops.jl")
include("utilities/symmetricpacked.jl")
include("utilities/uniformarrays.jl")
include("utilities/vecnormdiff.jl")
include("utilities/normdiff.jl")

# Basic functions

Expand All @@ -38,6 +46,7 @@ include("functions/indBallRank.jl")
include("functions/indBinary.jl")
include("functions/indBox.jl")
include("functions/indFree.jl")
include("functions/indGraph.jl")
include("functions/indHalfspace.jl")
include("functions/indNonnegative.jl")
include("functions/indNonpositive.jl")
Expand All @@ -52,16 +61,14 @@ include("functions/leastSquares.jl")
include("functions/linear.jl")
include("functions/logBarrier.jl")
include("functions/logisticLoss.jl")
include("functions/maximum.jl")
include("functions/normL2.jl")
include("functions/normL0.jl")
include("functions/normL1.jl")
include("functions/normL2.jl")
include("functions/normL21.jl")
include("functions/normL0.jl")
include("functions/nuclearNorm.jl")
include("functions/quadratic.jl")
include("functions/sqrNormL2.jl")
include("functions/sumPositive.jl")
include("functions/indGraph.jl")
include("functions/sqrHingeLoss.jl")
include("functions/crossEntropy.jl")

Expand All @@ -82,10 +89,11 @@ include("calculus/tilt.jl")
include("calculus/translate.jl")
include("calculus/sum.jl")

# Functions obtained from basic + calculus
# Functions obtained from basic (as special cases or using calculus rules)

include("functions/hingeLoss.jl")
include("functions/indExp.jl")
include("functions/maximum.jl")
include("functions/normLinf.jl")
include("functions/sumLargest.jl")

Expand Down Expand Up @@ -126,9 +134,8 @@ Return values:
* `y`: the proximal point ``y``
* `fy`: the value ``f(y)``
"""

function prox(f::ProximableFunction, x, gamma=1.0)
y = deepsimilar(x)
function prox(f::ProximableFunction, x::ArrayOrTuple{R}, gamma=one(R)) where R
y = similar(x)
fy = prox!(y, f, x, gamma)
return y, fy
end
Expand All @@ -147,8 +154,9 @@ The resulting point ``y`` is written to the (pre-allocated) array `y`, which mus
Return values:
* `fy`: the value ``f(y)``
"""

prox!
# TODO: should we put the following here instead? And remove the default value for gamma in each subtype
# prox!(y::ArrayOrTuple{R}, f::ProximableFunction, x::ArrayOrTuple{R}) = prox!(y, f, x, one(R))

"""
**Gradient mapping**
Expand All @@ -161,9 +169,8 @@ Return values:
* `gradfx`: the (sub)gradient of ``f`` at ``x``
* `fx`: the value ``f(x)``
"""

function gradient(f::ProximableFunction, x)
y = deepsimilar(x)
y = similar(x)
fx = gradient!(y, f, x)
return y, fx
end
Expand All @@ -178,7 +185,6 @@ Writes ``\\nabla f(x)`` to `gradfx`, which must be pre-allocated and have the sa
Return values:
* `fx`: the value ``f(x)``
"""

gradient!

end
19 changes: 9 additions & 10 deletions src/calculus/conjugate.jl
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ Returns the convex conjugate (also known as Fenchel conjugate, or Fenchel-Legend
f^*(x) = \\sup_y \\{ \\langle y, x \\rangle - f(y) \\}.
```
"""

struct Conjugate{T <: ProximableFunction} <: ProximableFunction
f::T
function Conjugate{T}(f::T) where {T<: ProximableFunction}
Expand All @@ -38,13 +37,13 @@ Conjugate(f::T) where {T <: ProximableFunction} = Conjugate{T}(f)
# only prox! is provided here, call method would require being able to compute
# an element of the subdifferential of the conjugate

function prox!(y::AbstractArray{R}, g::Conjugate, x::AbstractArray{R}, gamma::Real=1.0) where R <: Real
function prox!(y::AbstractArray{R}, g::Conjugate, x::AbstractArray{R}, gamma::R=one(R)) where R <: Real
# Moreau identity
v = prox!(y, g.f, x/gamma, 1.0/gamma)
v = prox!(y, g.f, x/gamma, one(R)/gamma)
if is_set(g)
v = 0.0
v = zero(R)
else
v = vecdot(x,y) - gamma*vecdot(y,y) - v
v = dot(x,y) - gamma*dot(y,y) - v
end
for k in eachindex(y)
y[k] = x[k] - gamma*y[k]
Expand All @@ -54,12 +53,12 @@ end

# complex case, need to cast inner products to real

function prox!(y::AbstractArray{Complex{R}}, g::Conjugate, x::AbstractArray{Complex{R}}, gamma::Real=1.0) where R <: Real
v = prox!(y, g.f, x/gamma, 1.0/gamma)
function prox!(y::AbstractArray{Complex{T}}, g::Conjugate, x::AbstractArray{Complex{T}}, gamma::R=one(R)) where {R <: Real, T <: RealOrComplex{R}}
v = prox!(y, g.f, x/gamma, one(R)/gamma)
if is_set(g)
v = 0.0
v = zero(R)
else
v = real(vecdot(x,y)) - gamma*real(vecdot(y,y)) - v
v = real(dot(x,y)) - gamma*real(dot(y,y)) - v
end
for k in eachindex(y)
y[k] = x[k] - gamma*y[k]
Expand All @@ -71,5 +70,5 @@ end

function prox_naive(g::Conjugate, x::AbstractArray{T}, gamma::Real=1.0) where T <: RealOrComplex
y, v = prox_naive(g.f, x/gamma, 1.0/gamma)
return x - gamma*y, real(vecdot(x,y)) - gamma*real(vecdot(y,y)) - v
return x - gamma*y, real(dot(x,y)) - gamma*real(dot(y,y)) - v
end
Loading

0 comments on commit cf120a3

Please sign in to comment.