-
Notifications
You must be signed in to change notification settings - Fork 27
Expand file tree
/
Copy pathprecompose.jl
More file actions
99 lines (87 loc) · 3.1 KB
/
precompose.jl
File metadata and controls
99 lines (87 loc) · 3.1 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
# Precompose with a linear mapping + translation (= affine)
export Precompose
"""
Precompose(f, L, μ, b)
Return the function
```math
g(x) = f(Lx + b)
```
where ``f`` is a convex function and ``L`` is a linear mapping: this must
satisfy ``LL^* = μI`` for ``μ > 0``. Furthermore, either ``f`` is separable or
parameter `μ` is a scalar, for the `prox` of ``g`` to be computable.
Parameter `L` defines ``L`` through the `mul!` method. Therefore `L` can be an
`AbstractMatrix` for example, but not necessarily.
In this case, `prox` and `prox!` are computed according to Prop. 24.14 in
Bauschke, Combettes "Convex Analysis and Monotone Operator Theory in Hilbert
Spaces", 2nd edition, 2016. The same result is Prop. 23.32 in the 1st edition
of the same book.
"""
struct Precompose{T, M, U, V}
f::T
L::M
mu::U
b::V
function Precompose{T, M, U, V}(f::T, L::M, mu::U, b::V) where {T, M, U, V}
if !is_convex(f)
error("f must be convex")
end
if any(mu .<= 0)
error("elements of μ must be positive")
end
new(f, L, mu, b)
end
end
is_proximable(::Type{<:Precompose{T}}) where T = is_proximable(T)
is_convex(::Type{<:Precompose{T}}) where T = is_convex(T)
is_set_indicator(::Type{<:Precompose{T}}) where T = is_set_indicator(T)
is_singleton_indicator(::Type{<:Precompose{T}}) where T = is_singleton_indicator(T)
is_cone_indicator(::Type{<:Precompose{T}}) where T = is_cone_indicator(T)
is_affine_indicator(::Type{<:Precompose{T}}) where T = is_affine_indicator(T)
is_smooth(::Type{<:Precompose{T}}) where T = is_smooth(T)
is_locally_smooth(::Type{<:Precompose{T}}) where T = is_locally_smooth(T)
is_generalized_quadratic(::Type{<:Precompose{T}}) where T = is_generalized_quadratic(T)
is_strongly_convex(::Type{<:Precompose{T}}) where T = is_strongly_convex(T)
Precompose(f::T, L::M, mu::U, b::V) where {T, M, U, V} = Precompose{T, M, U, V}(f, L, mu, b)
Precompose(f::T, L::M, mu::U) where {T, M, U} = Precompose(f, L, mu, 0)
function (g::Precompose)(x)
return g.f(g.L * x .+ g.b)
end
function gradient!(y, g::Precompose, x)
res = g.L*x
if g.b != 0
res .+= g.b
end
gradres = similar(res)
v = gradient!(gradres, g.f, res)
mul!(y, adjoint(g.L), gradres)
return v
end
function prox!(y, g::Precompose, x, gamma)
# See Prop. 24.14 in Bauschke, Combettes
# "Convex Analysis and Monotone Operator Theory in Hilbert Spaces",
# 2nd ed., 2016.
#
# The same result is Prop. 23.32 in the 1st ed. of the same book.
#
# This case has an additional translation: if f(x) = h(x + b) then
# prox_f(x) = prox_h(x + b) - b
# Then one can apply the above mentioned result to g(x) = f(Lx).
#
res = g.L * x
if g.b != 0
res .+= g.b
end
proxres = similar(res)
v = prox!(proxres, g.f, res, g.mu.*gamma)
proxres .-= res
proxres ./= g.mu
mul!(y, adjoint(g.L), proxres)
y .+= x
return v
end
function prox_naive(g::Precompose, x, gamma)
res = g.L*x .+ g.b
proxres, v = prox_naive(g.f, res, g.mu .* gamma)
y = x + g.L'*((proxres .- res)./g.mu)
return y, v
end