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

Proximal of LInfty wrong? #1613

Open
mehrhardt opened this issue Oct 20, 2021 · 7 comments
Open

Proximal of LInfty wrong? #1613

mehrhardt opened this issue Oct 20, 2021 · 7 comments

Comments

@mehrhardt
Copy link
Contributor

As it is implemented, the proximal of the Linfty-norm does not depend on its scaling factor in front of it (see line above). Not sure what the correct formulation is but the one implemented has to be wrong. This is related to the missing tests as in #1385.

@mehrhardt
Copy link
Contributor Author

My current guess is that it should be

        def _call(self, x, out):
            """Return ``self(x)``."""

            if x is out:
                x = x.copy()

            proj_l1(x, self.sigma, out)

but I didn't have time to verify it.

Anyone knows the answer?

@mehrhardt
Copy link
Contributor Author

As it is implemented, the proximal of the Linfty-norm does not depend on its scaling factor in front of it (see line above). Not sure what the correct formulation is but the one implemented has to be wrong.

One way to see this is for sigma = 0 where one expects to get the identity as the prox.

@ozanoktem
Copy link
Contributor

@JevgenijaAksjonova or @sbanert, do you have any input regarding the mathematical question that @mehrhardt posted?

@mehrhardt
Copy link
Contributor Author

I think I figured what the prox should be:

        def _call(self, x, out):
            """Return ``self(x)``."""

            if x is out:
                x = x.copy()

            proj_l1(x, self.sigma, out)

            out.lincomb(-1, out, 1, x)

but is there a good way to test whether this is correct?

@mehrhardt
Copy link
Contributor Author

mehrhardt commented Oct 21, 2021

It turns out that the issue is even deeper than I thought. The following example reveals that the projection onto L1 balls is incorrect.

import odl

X = odl.rn(2)

in0 = X.one().copy()

print(f(in0))
for sigma in [0, 1, 10]:

    f = odl.solvers.L1Norm(X)
    out = odl.solvers.nonsmooth.proj_l1(in0, sigma)
        
    print(sigma, f(out))

2.0
0 0.0
1 1.0
10 10.0

@mehrhardt
Copy link
Contributor Author

OK, I believe I figured it out :D

The issue is the projection onto the L1-ball which should have been

    if out is None:
        out = x.space.element()

    u = x.ufuncs.absolute()
    
    if u.ufuncs.sum() <= radius:
        out[:] = x
    else:
        v = x.ufuncs.sign()
        proj_simplex(u, radius, out)
        out *= v

    return out

where in the original ODL code the "if" was missing. I'll make a PR.

@mehrhardt
Copy link
Contributor Author

PR is now open #1614

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

No branches or pull requests

2 participants