Skip to content

Commit

Permalink
Fix warnings and test failures on Julia 1.5 (JuliaFirstOrder#106)
Browse files Browse the repository at this point in the history
* fix warnings and test failures in julia 1.5

* fix more warnings

* introduce is_support to fix conjugate

* fix rtol for inaccurate prox

* fix pypo

* fix isapprox for tuples
  • Loading branch information
lostella authored Apr 30, 2020
1 parent 185073a commit 65af052
Show file tree
Hide file tree
Showing 17 changed files with 120 additions and 88 deletions.
1 change: 1 addition & 0 deletions src/ProximalOperators.jl
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,7 @@ is_singleton(f::ProximableFunction) = false
is_cone(f::ProximableFunction) = false
is_affine(f::ProximableFunction) = is_singleton(f)
is_set(f::ProximableFunction) = is_cone(f) || is_affine(f)
is_support(f::ProximableFunction) = is_convex(f) && is_cone(f)
is_smooth(f::ProximableFunction) = false
is_quadratic(f::ProximableFunction) = false
is_generalized_quadratic(f::ProximableFunction) = is_quadratic(f) || is_affine(f)
Expand Down
4 changes: 3 additions & 1 deletion src/calculus/conjugate.jl
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@ is_smooth(f::Conjugate) = is_strongly_convex(f.f)
is_strongly_convex(f::Conjugate) = is_smooth(f.f)
is_quadratic(f::Conjugate) = is_strongly_convex(f.f) && is_generalized_quadratic(f.f)
is_generalized_quadratic(f::Conjugate) = is_quadratic(f.f)
is_set(f::Conjugate) = is_convex(f.f) && is_support(f.f)
is_support(f::Conjugate) = is_convex(f.f) && is_set(f.f)

fun_dom(f::Conjugate) = fun_dom(f.f)

Expand Down Expand Up @@ -66,7 +68,7 @@ end

function prox_naive(g::Conjugate, x::AbstractArray{T}, gamma=R(1)) where {R, T <: RealOrComplex{R}}
y, v = prox_naive(g.f, x/gamma, 1/gamma)
return x - gamma * y, real(dot(x, y)) - gamma * real(dot(y, y)) - v
return x - gamma * y, if is_set(g) R(0) else real(dot(x, y)) - gamma * real(dot(y, y)) - v end
end

# TODO: hard-code conjugation rules? E.g. precompose/epicompose
2 changes: 1 addition & 1 deletion src/functions/indSphereL2.jl
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ is_set(f::IndSphereL2) = true
IndSphereL2(r::R=1.0) where {R <: Real} = IndSphereL2{R}(r)

function (f::IndSphereL2)(x::AbstractArray{T}) where {R <: Real, T <: RealOrComplex{R}}
if abs(norm(x) - f.r) > f.r*eps(R)
if abs(norm(x) - f.r) / f.r > 100 * eps(R)
return R(Inf)
end
return R(0)
Expand Down
1 change: 1 addition & 0 deletions src/functions/normL1.jl
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ end

is_separable(f::NormL1) = true
is_convex(f::NormL1) = true
is_support(f::NormL1) = true

"""
NormL1(λ::Real=1.0)
Expand Down
1 change: 1 addition & 0 deletions src/functions/normL2.jl
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ struct NormL2{R <: Real} <: ProximableFunction
end

is_convex(f::NormL2) = true
is_support(f::NormL2) = true

NormL2(lambda::R=1.0) where {R <: Real} = NormL2{R}(lambda)

Expand Down
2 changes: 1 addition & 1 deletion src/utilities/tuples.jl
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

import Base: isapprox, similar, zero

isapprox(x::Tuple, y::Tuple) = all(isapprox.(x, y))
isapprox(x::Tuple, y::Tuple, args...; kwargs...) = all(isapprox.(x, y, args...; kwargs...))

similar(x::Tuple) = similar.(x)

Expand Down
12 changes: 7 additions & 5 deletions test/runtests.jl
Original file line number Diff line number Diff line change
Expand Up @@ -44,10 +44,12 @@ function prox_test(f, x::ArrayOrTuple{R}, gamma=R(1)) where R <: Real
y_naive, fy_naive = ProximalOperators.prox_naive(f, x, gamma)

@test typeof(fy_naive) == R

rtol = if ProximalOperators.is_prox_accurate(f) sqrt(eps(R)) else 1e-4 end

if ProximalOperators.is_convex(f)
@test y_prealloc y
@test y_naive y
@test isapprox(y_prealloc, y, rtol=rtol)
@test isapprox(y_naive, y, rtol=rtol)
if ProximalOperators.is_set(f)
@test fy_prealloc == 0
end
Expand All @@ -69,11 +71,11 @@ end
# i.e., that more specific properties imply less specific ones
# e.g., the indicator of a subspace is the indicator of a set in particular
function predicates_test(f)
# is_quadratic => is_(generalized_quadratic && smooth)
# quadratic => generalized_quadratic && smooth
@test !is_quadratic(f) || (is_generalized_quadratic(f) && is_smooth(f))
# is_(singleton || cone || affine) => is_set
# (singleton || cone || affine) => set
@test !(is_singleton(f) || is_cone(f) || is_affine(f)) || is_set(f)
# is_strongly_convex => is_convex
# strongly_convex => convex
@test !is_strongly_convex(f) || is_convex(f)
end

Expand Down
69 changes: 34 additions & 35 deletions test/test_calculus.jl
Original file line number Diff line number Diff line change
@@ -1,123 +1,122 @@
# Test equivalence of functions and prox mappings by means of calculus rules

using LinearAlgebra
using Random

Random.seed!(0)
using ProximalOperators
using Test

stuff = [
Dict( "funcs" => (IndBallLinf(), Conjugate(NormL1())),
"args" => ( randn(10), ),
"gammas" => ( 1.0, )
),

Dict( "funcs" => (lambda -> (NormL1(lambda), Conjugate(IndBallLinf(lambda))))(0.1+10.0*rand()),
Dict( "funcs" => (lambda -> (NormL1(lambda), Conjugate(IndBallLinf(lambda))))(0.1 + 10.0*rand()),
"args" => ( 5.0*sign.(randn(10)) + 5.0*randn(10),
5.0*sign.(randn(20)) + 5.0*randn(20) ),
"gammas" => ( 0.5+rand(), 0.5+rand() )
),

Dict( "funcs" => (lambda -> (IndBallLinf(lambda), Conjugate(NormL1(lambda))))(0.1+10.0*rand()),
Dict( "funcs" => (lambda -> (IndBallLinf(lambda), Conjugate(NormL1(lambda))))(0.1 + 10.0*rand()),
"args" => ( 5.0*sign.(randn(10)) + 5.0*randn(10),
5.0*sign.(randn(20)) + 5.0*randn(20) ),
"gammas" => ( 0.5+rand(), 0.5+rand() )
),

Dict( "funcs" => (lambda -> (NormL1(lambda), Conjugate(IndBox(-lambda,lambda))))(0.1 .+ 10.0*rand(30)),
"args" => ( 5.0*sign.(randn(30)) + 5.0*randn(30), ),
"gammas" => ( 0.5+rand(), 0.5+rand() )
),

Dict( "funcs" => (lambda -> (IndBox(-lambda,lambda), Conjugate(NormL1(lambda))))(0.1 .+ 10.0*rand(30)),
"args" => ( 5.0*sign.(randn(30)) + 5.0*randn(30), ),
"gammas" => ( 0.5+rand(), 0.5+rand() )
),

Dict( "funcs" => (lambda -> (NormL2(lambda), Conjugate(IndBallL2(lambda))))(0.1 .+ 10.0*rand()),
"args" => ( 5.0*sign.(randn(10)) + 5.0*randn(10),
5.0*sign.(randn(20)) + 5.0*randn(20) ),
"gammas" => ( 0.5+rand(), 0.5+rand() )
),

Dict( "funcs" => (lambda -> (IndBallL2(lambda), Conjugate(NormL2(lambda))))(0.1 .+ 10.0*rand()),
"args" => ( 5.0*sign.(randn(10)) + 5.0*randn(10),
5.0*sign.(randn(20)) + 5.0*randn(20) ),
"gammas" => ( 0.5+rand(), 0.5+rand() )
),

Dict( "funcs" => ((a, b, mu) -> (LogBarrier(a, b, mu), Postcompose(PrecomposeDiagonal(LogBarrier(), a, b), mu)))(2.0, 0.5, 1.0),
"args" => ( rand(10), rand(10) ),
"gammas" => ( 0.5+rand(), 0.5+rand() )
),

Dict( "funcs" => (p -> (IndPoint(p), IndBox(p, p)))(randn(50)),
"args" => ( randn(50), randn(50), randn(50) ),
"gammas" => ( 1.0, rand(), 5.0*rand() )
),

Dict( "funcs" => (IndZero(), IndBox(0, 0)),
"args" => ( randn(50), randn(50), randn(50) ),
"gammas" => ( 1.0, rand(), 5.0*rand() )
),

Dict( "funcs" => (IndFree(), IndBox(-Inf, +Inf)) ,
"args" => ( randn(50), randn(50), randn(50) ),
"gammas" => ( 1.0, rand(), 5.0*rand() )
),

Dict( "funcs" => (IndNonnegative(), IndBox(0.0, Inf)),
"args" => ( randn(50), randn(50), randn(50) ),
"gammas" => ( 1.0, rand(), 5.0*rand() )
),

Dict( "funcs" => (IndNonpositive(), IndBox(-Inf, 0.0)),
"args" => ( randn(50), randn(50), randn(50) ),
"gammas" => ( 1.0, rand(), 5.0*rand() )
),

Dict( "funcs" => (lambda -> (SqrNormL2(lambda), Conjugate(SqrNormL2(1.0/lambda))))(0.1 .+ 5.0*rand()),
"args" => ( randn(50), randn(50), randn(50) ),
"gammas" => ( 1.0, rand(), 5.0*rand() )
),

Dict( "funcs" => ((A, b) -> (LeastSquares(A, b), Tilt(LeastSquares(A, zeros(size(A, 1))), -A'*b, 0.5*dot(b, b))))(randn(10,20), randn(10)),
"args" => ( randn(20), randn(20), randn(20) ),
"gammas" => ( 1.0, rand(), 5.0*rand() )
),

Dict( "funcs" => ((lambda, rho) -> (ElasticNet(lambda,rho), Regularize(NormL1(lambda),rho)))(rand(), rand()),
"args" => ( randn(20), randn(20), randn(20) ),
"gammas" => ( 1.0, rand(), 5.0*rand() )
),

Dict( "funcs" => ((b, mu) -> (HingeLoss(b, mu), Postcompose(PrecomposeDiagonal(SumPositive(), -b, 1.0), mu)))([0.5 .+ rand(10); -0.5 .- rand(10)], 0.5+rand()),
"args" => ( randn(20), randn(20), randn(20) ),
"gammas" => ( 1.0, rand(), 5.0*rand() )
),

Dict( "funcs" => ((A, b) -> (Postcompose(LeastSquares(A, b), 15.0, 6.5), Postcompose(Postcompose(LeastSquares(A, b), 5.0, 1.5), 3.0, 2.0)))(randn(10, 20), randn(10)),
"args" => ( randn(20), randn(20), randn(20) ),
"gammas" => ( 1.0, rand(), 5.0*rand() )
)
]

for i = 1:length(stuff)
f = stuff[i]["funcs"][1]
g = stuff[i]["funcs"][2]
@testset "$i" for i = 1:length(stuff)
f = stuff[i]["funcs"][1]
g = stuff[i]["funcs"][2]

for j = 1:length(stuff[i]["args"])
x = stuff[i]["args"][j]
gamma = stuff[i]["gammas"][j]
for j = 1:length(stuff[i]["args"])
x = stuff[i]["args"][j]
gamma = stuff[i]["gammas"][j]

# compare the three versions (for f)
yf, fy = prox_test(f, x, gamma)
# compare the three versions (for f)
yf, fy = prox_test(f, x, gamma)

# compare the three versions (for g)
yg, gy = prox_test(g, x, gamma)
# compare the three versions (for g)
yg, gy = prox_test(g, x, gamma)

# compare results of f and g
@test norm(yf-yg, Inf)/(1+norm(yf, Inf)) <= TOL_ASSERT
@test fy == gy || abs(fy-gy)/(1+abs(fy)) <= TOL_ASSERT
end
# compare results of f and g
@test norm(yf-yg, Inf)/(1+norm(yf, Inf)) <= TOL_ASSERT
@test fy == gy || abs(fy-gy)/(1+abs(fy)) <= TOL_ASSERT
end

end
30 changes: 13 additions & 17 deletions test/test_condition.jl
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
# test whether prox satisfies some conditions

using Random
using LinearAlgebra
using SparseArrays

Random.seed!(0)
using ProximalOperators
using Test

stuff = [
Dict( "constr" => LeastSquares,
Expand Down Expand Up @@ -48,18 +47,15 @@ stuff = [
),
]

for i = 1:length(stuff)
constr = stuff[i]["constr"]
params = stuff[i]["params"]
args = stuff[i]["args"]
gammas = stuff[i]["gammas"]
test = stuff[i]["test"]
for i = 1:length(params)
# println("----------------------------------------------------------")
# println(constr)
f = constr(params[i]...)
# println(f)
y, fy = prox(f, args[i], gammas[i])
@test test(f, args[i], gammas[i], y)
end
@testset "$(d["constr"])" for d in stuff
constr = d["constr"]
params = d["params"]
args = d["args"]
gammas = d["gammas"]
test = d["test"]
for i = 1:length(params)
f = constr(params[i]...)
y, fy = prox(f, args[i], gammas[i])
@test test(f, args[i], gammas[i], y)
end
end
20 changes: 18 additions & 2 deletions test/test_equivalences.jl
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,17 @@

using Random
using SparseArrays

Random.seed!(0)
using ProximalOperators
using Test

################################################################################
### testing consistency of simplex/L1 ball
################################################################################
# Inspired by Condat, "Fast projection onto the simplex and the l1 ball", Mathematical Programming, 158:575–585, 2016.
# See Prop. 2.1 there and following remarks.

@testset "IndSimplex/IndBallL1" begin

n = 20
N = 10

Expand Down Expand Up @@ -41,10 +43,14 @@ for i = 1:N
@test y1 y2
end

end

################################################################################
### testing consistency of hinge loss/box indicator
################################################################################

@testset "HingeLoss/IndBox" begin

n = 20
N = 10

Expand All @@ -70,10 +76,14 @@ for i = 1:N
@test y1 y2
end

end

################################################################################
### testing regularize
################################################################################

@testset "Regularize/ElasticNet" begin

lambda = rand()
rho = rand()
g = Regularize(NormL1(lambda),rho)
Expand All @@ -85,10 +95,14 @@ y2,f2 = prox(ElasticNet(lambda,rho),x)
@test f f2
@test y y2

end

################################################################################
### testing IndAffine
################################################################################

@testset "IndAffine (sparse/dense)" begin

A = sprand(50,100, 0.1)
b = randn(50)

Expand All @@ -101,3 +115,5 @@ y2, f2 = prox(g2, x)

@test f1 f2
@test y1 y2

end
7 changes: 5 additions & 2 deletions test/test_huberLoss.jl
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
using LinearAlgebra
using Random
using ProximalOperators
using Test

Random.seed!(0)
@testset "HuberLoss" begin

f = HuberLoss(1.5, 0.7)

Expand Down Expand Up @@ -30,3 +31,5 @@ grad_fx, fx = gradient(f, x)

@test abs(fx - f(x)) <= 1e-12
@test norm(0.7*x - grad_fx, Inf) <= 1e-12

end
6 changes: 5 additions & 1 deletion test/test_indAffine.jl
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
using LinearAlgebra
using SparseArrays
using Random
using ProximalOperators
using Test

Random.seed!(0)
@testset "IndAffine" begin

# Full matrix

Expand Down Expand Up @@ -49,3 +51,5 @@ call_test(f, x)
y, fy = prox_test(f, x)

@test f(y) == 0.0

end
Loading

0 comments on commit 65af052

Please sign in to comment.