|
1 | | -################################################################################ |
2 | | -# Davis-Yin splitting iterable |
3 | | -# |
4 | | -# See: |
5 | | -# [1] Davis, Yin "A Three-Operator Splitting Scheme and its Optimization Applications", |
6 | | -# Set-Valued and Variational Analysis, vol. 25, no. 4, pp 829–858 (2017). |
7 | | -# |
| 1 | +# Davis, Yin. "A Three-Operator Splitting Scheme and its Optimization |
| 2 | +# Applications", Set-Valued and Variational Analysis, vol. 25, no. 4, |
| 3 | +# pp. 829–858 (2017). |
8 | 4 |
|
9 | 5 | using Base.Iterators |
10 | 6 | using ProximalAlgorithms.IterationTools |
@@ -64,60 +60,90 @@ function Base.iterate(iter::DYS_iterable, state::DYS_state) |
64 | 60 | return state, state |
65 | 61 | end |
66 | 62 |
|
| 63 | +# Solver |
| 64 | + |
| 65 | +struct DavisYin{R} |
| 66 | + gamma::Maybe{R} |
| 67 | + lambda::R |
| 68 | + maxit::Int |
| 69 | + tol::R |
| 70 | + verbose::Bool |
| 71 | + freq::Int |
| 72 | + |
| 73 | + function DavisYin{R}(; gamma::Maybe{R}=nothing, lambda::R=R(1.0), |
| 74 | + maxit::Int=10000, tol::R=R(1e-8), verbose::Bool=false, freq::Int=100 |
| 75 | + ) where R |
| 76 | + @assert gamma === nothing || gamma > 0 |
| 77 | + @assert lambda > 0 |
| 78 | + @assert maxit > 0 |
| 79 | + @assert tol > 0 |
| 80 | + @assert freq > 0 |
| 81 | + new(gamma, lambda, maxit, tol, verbose, freq) |
| 82 | + end |
| 83 | +end |
| 84 | + |
| 85 | +function (solver::DavisYin{R})(x0::AbstractArray{C}; |
| 86 | + f=Zero(), g=Zero(), h=Zero(), A=I, L::Maybe{R}=nothing |
| 87 | +) where {R, C <: Union{R, Complex{R}}} |
| 88 | + |
| 89 | + stop(state::DYS_state) = norm(state.res, Inf) <= solver.tol |
| 90 | + disp((it, state)) = @printf("%5d | %.3e\n", it, norm(state.res, Inf)) |
| 91 | + |
| 92 | + if solver.gamma === nothing |
| 93 | + if L !== nothing |
| 94 | + gamma = R(1)/L |
| 95 | + else |
| 96 | + error("You must specify either L or gamma") |
| 97 | + end |
| 98 | + else |
| 99 | + gamma = solver.gamma |
| 100 | + end |
| 101 | + |
| 102 | + iter = DYS_iterable(f, g, h, A, x0, gamma, solver.lambda) |
| 103 | + iter = take(halt(iter, stop), solver.maxit) |
| 104 | + iter = enumerate(iter) |
| 105 | + if solver.verbose iter = tee(sample(iter, solver.freq), disp) end |
| 106 | + |
| 107 | + num_iters, state_final = loop(iter) |
| 108 | + |
| 109 | + return state_final.xf, state_final.xg, num_iters |
| 110 | + |
| 111 | +end |
| 112 | + |
| 113 | +# Outer constructors |
| 114 | + |
67 | 115 | """ |
68 | | - davisyin(x0; f, g, h, A, [...]) |
| 116 | + DavisYin([gamma, lambda, maxit, tol, verbose, freq]) |
69 | 117 |
|
70 | | -Solves convex optimization problems of the form |
| 118 | +Instantiate the Davis-Yin splitting algorithm (see [1]) for solving |
| 119 | +convex optimization problems of the form |
71 | 120 |
|
72 | 121 | minimize f(x) + g(x) + h(A x), |
73 | 122 |
|
74 | | -where `h` is smooth and `A` is a linear mapping, using the Davis-Yin splitting |
75 | | -algorithm, see [1]. |
76 | | -
|
77 | | -Either of the following arguments must be specified: |
| 123 | +where `h` is smooth and `A` is a linear mapping (for example, a matrix). |
| 124 | +If `solver = DavisYin(args...)`, then the above problem is solved with |
78 | 125 |
|
79 | | -* `L::Real`, Lipschitz constant of the gradient of `h(A x)`. |
80 | | -* `gamma:Real`, stepsize parameter. |
| 126 | + solver(x0; [f, g, h, A]) |
81 | 127 |
|
82 | | -Other optional keyword arguments: |
| 128 | +Optional keyword arguments: |
83 | 129 |
|
| 130 | +* `gamma::Real` (default: `nothing`), stepsize parameter. |
84 | 131 | * `labmda::Real` (default: `1.0`), relaxation parameter, see [1]. |
85 | 132 | * `maxit::Integer` (default: `1000`), maximum number of iterations to perform. |
86 | 133 | * `tol::Real` (default: `1e-8`), absolute tolerance on the fixed-point residual. |
87 | 134 | * `verbose::Bool` (default: `true`), whether or not to print information during the iterations. |
88 | 135 | * `freq::Integer` (default: `100`), frequency of verbosity. |
89 | 136 |
|
| 137 | +If `gamma` is not specified at construction time, the following keyword |
| 138 | +argument must be specified at solve time: |
| 139 | +
|
| 140 | +* `L::Real`, Lipschitz constant of the gradient of `h(A x)`. |
| 141 | +
|
90 | 142 | References: |
91 | 143 |
|
92 | 144 | [1] Davis, Yin. "A Three-Operator Splitting Scheme and its Optimization |
93 | 145 | Applications", Set-Valued and Variational Analysis, vol. 25, no. 4, |
94 | 146 | pp. 829–858 (2017). |
95 | 147 | """ |
96 | | -function davisyin(x0; |
97 | | - f=Zero(), g=Zero(), h=Zero(), A=I, |
98 | | - lambda=1.0, L=nothing, gamma=nothing, |
99 | | - maxit=10_000, tol=1e-8, |
100 | | - verbose=false, freq=100) |
101 | | - |
102 | | - R = real(eltype(x0)) |
103 | | - |
104 | | - stop(state::DYS_state) = norm(state.res, Inf) <= R(tol) |
105 | | - disp((it, state)) = @printf("%5d | %.3e\n", it, norm(state.res, Inf)) |
106 | | - |
107 | | - if gamma === nothing |
108 | | - if L !== nothing |
109 | | - gamma = R(1)/R(L) |
110 | | - else |
111 | | - error("You must specify either L or gamma") |
112 | | - end |
113 | | - end |
114 | | - |
115 | | - iter = DYS_iterable(f, g, h, A, x0, R(gamma), R(lambda)) |
116 | | - iter = take(halt(iter, stop), maxit) |
117 | | - iter = enumerate(iter) |
118 | | - if verbose iter = tee(sample(iter, freq), disp) end |
119 | | - |
120 | | - num_iters, state_final = loop(iter) |
121 | | - |
122 | | - return state_final.xf, state_final.xg, num_iters |
123 | | -end |
| 148 | +DavisYin(::Type{R}; kwargs...) where R = DavisYin{R}(; kwargs...) |
| 149 | +DavisYin(; kwargs...) = DavisYin(Float64; kwargs...) |
0 commit comments