Skip to content
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

Add ADMM #1198

Merged
merged 9 commits into from
Oct 21, 2017
Merged

Add ADMM #1198

merged 9 commits into from
Oct 21, 2017

Conversation

kohr-h
Copy link
Member

@kohr-h kohr-h commented Oct 18, 2017

Depending on the outcome of #1197, f and g may change roles.

The functions ``f`` and ``g`` in the problem definition. They
need to implement the ``proximal`` method.
L : linear `Operator`
The linear operator that should be applied before ``f``. Its range
Copy link
Member Author

Choose a reason for hiding this comment

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

f -> g

if callback is not None and not callable(callback):
raise TypeError('`callback` {} is not callable'.format(callback))

# Initialize dual variables
Copy link
Member Author

Choose a reason for hiding this comment

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

Dual is probably the wrong name since we never look at convex conjugates, Just u and z should do.

# Combine functionals, order must correspond to the operator K
g = odl.solvers.SeparableSum(l2_norm, l1_norm)

# --- Select solver parameters and solve using PDHG --- #
Copy link
Member Author

Choose a reason for hiding this comment

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

update

f.proximal(tau)(x, out=x)

# L x^(k+1) + u^k
L(x, out=tmp_ran)
Copy link
Contributor

Choose a reason for hiding this comment

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

What is the difference between linearized ADMM and PDHG? In any case, I am pretty sure it is possible to rewrite your code calling "L" only once per iteration.

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 main difference is that ADMM uses the proximal of f in contrast to the proximal of g^* as in PDHG, so it's not a primal-dual method in that sense. But it's possible that one can still interpret it that way, dunno.

Anyway, you're right, I should be able to scrap one L evaluation, potentially at the cost of a new temporary.

# x^k - (tau/sigma) L^*(Lx^k + u^k - z^k)
x.lincomb(1, x, -tau / sigma, tmp_dom)
# x^(k+1) = prox[tau*f](_)
f.proximal(tau)(x, out=x)
Copy link
Contributor

@mehrhardt mehrhardt Oct 19, 2017

Choose a reason for hiding this comment

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

I like this line as it is very elegant. However, currently some of ODL's functionals do not support this (input and output are the same variable), see #1181.

Copy link
Member Author

Choose a reason for hiding this comment

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

True, but this is not the place to implement workarounds. This code has to be able to assume that operators work with aliased input and output.

@kohr-h
Copy link
Member Author

kohr-h commented Oct 19, 2017

Obviously I should add the solver to the test suite.

Copy link
Member

@adler-j adler-j left a comment

Choose a reason for hiding this comment

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

Minor stuff, fix and merge.

angle_partition = odl.uniform_partition(0, np.pi, 360)
# Detector: uniformly sampled, n = 512, min = -30, max = 30
detector_partition = odl.uniform_partition(-30, 30, 512)
geometry = odl.tomo.Parallel2dGeometry(angle_partition, detector_partition)
Copy link
Member

Choose a reason for hiding this comment

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

I prefer if we use the parallel_beam_geometry helper in these small examples

l2_norm = odl.solvers.L2NormSquared(ray_trafo.range).translated(data)

# Isotropic TV-regularization i.e. the l1-norm
l1_norm = 0.015 * odl.solvers.L1Norm(gradient.range)
Copy link
Member

Choose a reason for hiding this comment

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

Isotropic would be GroupL1Norm

@@ -0,0 +1,87 @@
"""Total variation tomography using ADMM.
Copy link
Member

Choose a reason for hiding this comment

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

linearized?

Where ``A`` is a parallel beam ray transform, ``grad`` the spatial
gradient and ``g`` is given noisy data.

See the documentation of the `admm` method for further details.
Copy link
Member

Choose a reason for hiding this comment

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

admm_linearized?

# --- Select solver parameters and solve using PDHG --- #

# Estimated operator norm, add 10 percent to ensure ||K||_2^2 * sigma * tau < 1
op_norm = 1.1 * odl.power_method_opnorm(op)
Copy link
Member

Choose a reason for hiding this comment

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

Overall we should move towards op.norm() in these cases now that it's implemented.

Copy link
Member Author

Choose a reason for hiding this comment

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

Don't blame me for all the above, it's literally copied from the PDHG example (except for the admm typos).

Copy link
Member

Choose a reason for hiding this comment

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

I guess we could make a "mother of all PRs" to fix that

# tmp_ran <- L x^(k+1)
L(x, out=tmp_ran)
# z^(k+1) <- prox[sigma*g](L x^(k+1) + u^k)
g.proximal(sigma)(tmp_ran + u, out=z) # 1 copy here
Copy link
Member

Choose a reason for hiding this comment

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

why not tmp_ran += u on the line above?

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 need L(x) later to add the new u. The alternative would be to use another temporary, but that seems overkill to me.

Copy link
Member

Choose a reason for hiding this comment

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

Go with this for now

assert x == x0

# Check that a provided callback is actually called
class CallbackTest(Callback):
Copy link
Member

Choose a reason for hiding this comment

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

class CallbackTest(Callback):
    was_called = False
    def __call__(self, *args, **kwargs):
        self.was_called = True

@kohr-h
Copy link
Member Author

kohr-h commented Oct 21, 2017

Done fixing, will merge after CI

@kohr-h
Copy link
Member Author

kohr-h commented Oct 21, 2017

In particular, I'll keep the roles of f and g as they are now, and we can decide simultaneously what we're gonna do regarding #1197.

@kohr-h kohr-h merged commit 66f8e2a into odlgroup:master Oct 21, 2017
@kohr-h kohr-h deleted the admm branch October 21, 2017 09:24
@kohr-h kohr-h mentioned this pull request Nov 13, 2017
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.

3 participants