Skip to content

add a user define option for threshold in bregman #3

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 12 commits into from
May 6, 2022
Merged

Conversation

ziyiyin97
Copy link
Member

No description provided.

src/bregman.jl Outdated
@@ -124,7 +126,7 @@ function bregman(funobj::Function, x::AbstractArray{T}, options::BregmanParams,
# Update z variable
@. z = z + d
# Get λ at first iteration
i == 1 && (λ = abs(T(quantile(abs.(z), options.quantile))))
i == 1 && (sol.λ = λ = isnothing(options.lambda) ? abs(T(quantile(abs.(z), options.quantile))) : abs(T(options.lambda)))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's a lot of if in one line but I guess.

I'm still waiting on an answer as to why you would ever end up with a complex-valued x

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So that will be easy if I want to solve AC'x=b where x is the complex curvelet coefficients. It is easier for me to take this form if I want to do weightings on x later

Copy link
Member

@mloubout mloubout Apr 4, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is not the right way to use this as you are not solving the correct problem. You are always solving the L1-l2 elastic net on the curvelet coefficient so x is your image and is real. if you give z as the input and C' instead of C you are solving sparsity in the image domain.

If you want to do weighting you need to provide W*C instead of C

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK I see your point. However, I think they are different ways to form the optimization problem. Either way works because they should be mathematically equivalent to solve for either the image or its curvelet coefficients. So I still think a PR to fix the support of complex number is needed. Any thought?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Either way does not work. You are confusing mathematical equivalence and implementation equivalence you can't make a program magically understand a change of variable (which is what you are doing). The documentation fo this implementation is very explicit about what it solves.

I don't have an issue with complex number supports but you are not doing what you think you are doing and it has to be for the case where x is complex, the actual primal x.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The problem of provide W*C instead of C is that its adjoint is not inverse. Then during soft-thresholding we have to do a W inverse.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's because you are trying to do a weighted l1 which is a different problem. You could add weighted l1 with the standard identity as default but again you need to be careful about what problem you are solving.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK I agree what you are saying now but we don't have to add anything new: just solve AC'Wx = b and use the identity as the sparsity transform will do the work. The solution is C'WX in the end. That's much simpler than adding a weighted l1

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's not the same as you will computethe threshold on W*z not on z.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No we don't have to. The forward and adjoint of W can already bump the entries on support 2 times so these entries are much easier to pass the threshold

src/bregman.jl Outdated
@@ -124,15 +127,29 @@ function bregman(funobj::Function, x::AbstractArray{T}, options::BregmanParams,
# Update z variable
@. z = z + d
# Get λ at first iteration
i == 1 && (λ = abs(T(quantile(abs.(z), options.quantile))))
if i == 1
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is very ugly, it takes more space than the actual algorithm, make a proper function with dispatch for this.

src/bregman.jl Outdated
if length(λ) == 1
@printf("%10d %15.5e %15.5e %15.5e %15.5e \n",i, t, obj_fun, f, λ)
else
@printf("%10d %15.5e %15.5e %15.5e %5s \n",i, t, obj_fun, f, "vector")
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

that's not informative, choose a value like min/max/mean/ or tuple or something to give proper information

@@ -20,11 +20,11 @@ imgn= img .+ .01f0*randn(Float32, size(img))
b = A*vec(imgn)

# setup bregamn
opt = bregman_options(maxIter=200, verbose=2, quantile=.5, alpha=1, antichatter=true)
opt2 = bregman_options(maxIter=200, verbose=2, quantile=.5, alpha=1, antichatter=true, spg=true)
opt = bregman_options(maxIter=200, verbose=2, alpha=1, antichatter=true)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I really don't like this. Now there is options all over the place that completely defeat the purpose. And why is the TD moved

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I remove TD so that it's optional (default is identity matrix)

What do you mean "Now there is options all over the place that completely defeat the purpose"?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

like do you mean I should move these lambda, lambdafunc etc inside the bregmanparams?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Now there is the options and the additional kwargs. Like user have to set options then have to put new ones separately like lambda. I still think its a bit messy. Put back the old basic interface and use dispatch to properly set the other cases in a clean way not adding 50 kwargs

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can't dispatch over a quantile or a pre-set threshold. They are both a single number. Any thought?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

99% of what you added can be easily done via simple dispatch rather than all these additional keyword arguments on top of the options, for exampel would have takien you one line to define

bregman(A, x, b) = bregman(A, LinearAlgebra.I, x::Array{T}, b)

And everything satays clean an easy to use. Try to make an effort making it user friendly not "works for me" friendly. All the rest is the same can be easily done via dispatch and/or options preprocessing and setup.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ok thanks for your suggestion on this LinearAlgebra.I thing. I agree this works in a more reasonable structure.

What's your take on this threshold thing? Following your suggestion, I should add it in the bregmanparams, right? Since a pre-set threshold and a quantile are both single value. Can't dispatch.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Options are all kwargs so you can filter then and do so pre-setup based on input

@ziyiyin97
Copy link
Member Author

Now previous functionality still works

@@ -20,11 +20,11 @@ imgn= img .+ .01f0*randn(Float32, size(img))
b = A*vec(imgn)

# setup bregamn
opt = bregman_options(maxIter=200, verbose=2, quantile=.5, alpha=1, antichatter=true)
opt2 = bregman_options(maxIter=200, verbose=2, quantile=.5, alpha=1, antichatter=true, spg=true)
opt = bregman_options(maxIter=200, verbose=2, quantile=.5, alpha=1, antichatter=true, TD=W)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

at that point let's just put A and x and b in options too........

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the bregman function itself should show A and b (or the function to calculate function value and gradient) since we are solving a linear system. In terms of the sparsifying transform ... I slightly prefer putting it into the options (since we have different "options" for sparsifying transform)

Copy link
Member

@mloubout mloubout left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't mind moving TD to the options but this still needs cleanup

src/bregman.jl Outdated
bregman_options(;verbose=1, progTol=1e-8, maxIter=20, store_trace=false, antichatter=true, quantile=.95, alpha=.5, spg=false) =
BregmanParams(verbose, progTol, maxIter, store_trace, antichatter, quantile, alpha, spg)
bregman_options(;verbose=1, progTol=1e-8, maxIter=20, store_trace=false, antichatter=true, alpha=.5, spg=false, TD=LinearAlgebra.I, quantile=.95, λ=nothing, λfunc=nothing) =
BregmanParams(verbose, progTol, maxIter, store_trace, antichatter, alpha, spg, TD, quantile, λ, λfunc)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is still messy

  • Some of these options are not compatible
  • You wouldn't need to check all these if options.x below if you just properly filter the input options to setup the problem

src/bregman.jl Outdated
λfunc = z->quantile(abs.(z), options.quantile)
end
end
return bregman(funobj, x, options, λfunc)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

now you have to call the algo with λfunc both in options and as an input which you wuldn't with properly setup options

src/bregman.jl Outdated
end

function bregman(funobj::Function, TD, x::AbstractArray{T}, options=bregman_options()) where {T}
options.TD = TD
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Mutating structure and objects is in general very bad practice coding-wise, so this needs a proper deprecation warning.

src/bregman.jl Outdated
@@ -124,16 +160,14 @@ function bregman(funobj::Function, x::AbstractArray{T}, options::BregmanParams,
# Update z variable
@. z = z + d
# Get λ at first iteration
i == 1 && (λ = abs(T(quantile(abs.(z), options.quantile))))
(i == 1) && (sol.λ = λ = abs.(T.(λfunc(z))))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

or you can just use sol.λ everywhere

src/bregman.jl Outdated
"""
function bregman(funobj::Function, x::AbstractArray{T}, options::BregmanParams, TD=nothing) where {T}

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No blank line between docstring and function

src/bregman.jl Outdated
"""
function bregman(A, TD, x::Array{T}, b, options) where {T}

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No blank line between docstring and code

src/bregman.jl Outdated

"""
bregman(A, TD, x, b, options)
bregman(A, x, b; options)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

TD isn't defined anymore in problem def line 47

src/bregman.jl Outdated
end

"""
bregman_options(;verbose=1, optTol=1e-6, progTol=1e-8, maxIter=20
store_trace=false, linesearch=false, alpha=.25, spg=false)
store_trace=false, λ=.2, alpha=.25, spg=false)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

where does .2 comes from

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

just to inform user that you can do something like this

@@ -5,9 +5,9 @@ using LinearAlgebra

N1 = 100
N2 = div(N1, 2) + 5
A = randn(N1, N2)
A = randn(ComplexF32, N1, N2)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you want to add a case add an extra one do not modify an existing one especially if you are disabling options

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ok

@@ -20,8 +20,8 @@ function obj(x)
return fun, grad
end

opt = bregman_options(maxIter=200, progTol=0, verbose=2)
sol = bregman(obj, 1 .+ randn(N2), opt)
opt = bregman_options(maxIter=200, progTol=0, verbose=2, antichatter=false) # anti chatter now only works with real number
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This needs to raise a proper error if attempted. Why isn't it supported?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Anti-chatter needs to record number of -1/1 when the gradient fluctuates. If things are complex then there is no -1/1

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes there is no sign in complex which is why there is a different thresholding function for this case so the anti-chatter should follow it and use the angle instead of sign but I think it should follow

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hmm in anti-chatter there is a sum of -1/1. How do we do it with angle? A sum of unit vectors with different angles? And then do inner product to calculate the scaling of gradient?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Probably something like that. I don't mind it leaving it as a todo and see if can make something like that work, would assume it would by summing the angles. But would adda warning or error if someone tries it so that it's readable not a lengthy error trace

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

gotcha make sense

@ziyiyin97 ziyiyin97 requested a review from mloubout May 2, 2022 15:32
src/bregman.jl Outdated
"""
function bregman(A, TD, x::Array{T}, b, options) where {T}
function bregman(A, x::AbstractArray{T}, b::AbstractArray{T}, options::BregmanParams) where {T}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No! There is defaults for a reason

Copy link
Member

@mloubout mloubout left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Mostly there

src/bregman.jl Outdated
if ~isnothing(λ)
λfunc = z->λ
else
λfunc = z->SlimOptim.quantile(abs.(z), quantile)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Statistics.

src/bregman.jl Outdated
"""
function bregman(A, TD, x::Array{T}, b, options) where {T}
function bregman(A, x::AbstractArray{T}, b::AbstractArray{T}, options::BregmanParams=bregman_options()) where {T}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

AbstractVector no? May wanna add it to obj below as well

src/bregman.jl Outdated
@@ -97,7 +116,7 @@ function bregman(funobj::Function, x::AbstractArray{T}, options::BregmanParams,
# Result structure
sol = breglog(x, z)
# Initialize λ
λ = abs(T(0))
sol.λ = abs(T(0))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can't this be done in options init now?

@@ -36,3 +36,35 @@ part_nz = i -> norm(sol.x[i], 1)/N2
@test part_nz(inds) < 1f-1
@test part_n(ninds) < 1f-1
@test sol.residual/sol.r_trace[1] < 1f-1

# test complex
A = randn(ComplexF32, N1, N2)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For T in [float, complex]...

src/bregman.jl Outdated
norm(x - sol.x) < options.progTol && (@printf("Step size below progTol\n"); break;)
update!(sol; iter=i, ϕ=obj_fun, residual=f, x=x, z=z, g=g, store_trace=options.store_trace)
end
return sol
end

function bregman(funobj::Function, x::AbstractArray{T}, options::BregmanParams, TD) where {T}
@warn "deprecation warning: TD should be put in BregmanParams when version >= 0.1.8; now overwritting TD in BregmanParams"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add new syntax explicitly to message

@ziyiyin97 ziyiyin97 requested a review from mloubout May 6, 2022 20:17
@mloubout mloubout merged commit c43c82b into master May 6, 2022
@mloubout mloubout deleted the bregman branch May 6, 2022 22:41
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants