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

Implement complex -> real functionals (e.g. L2Norm) #1328

Open
miallo opened this issue Apr 25, 2018 · 5 comments
Open

Implement complex -> real functionals (e.g. L2Norm) #1328

miallo opened this issue Apr 25, 2018 · 5 comments

Comments

@miallo
Copy link
Contributor

miallo commented Apr 25, 2018

  1. I remember from my courses in analytical geometry, that any norm of any vectorspace always maps to the real numbers. Is there a reason to return the range of a norm of a complex space as ComplexNumbers?
>>> X = odl.uniform_discr(0, 1, 10, dtype='complex')
>>> L2NormSquared = odl.solvers.L2NormSquared(X)
>>> print(L2NormSquared.range)
ComplexNumbers

(the example holds true for L1Norm and L2Norm as well)
2. And I have another problem: I want to create a functional that maps from the complex space to the real numbers (so I can minimise it). But the implementation of odl.solvers.Functional says that it maps to space.field (which for me would be the complex numbers). What can I do? Should I add an additional operator just to get the real part?
3. And if so: when trying odl.RealPart(l2NormSquared.range) the result is 'ComplexNumbers' object has no attribute 'real_space'. For now I could live without the functional, but in the future I would want to optimise over a parameter in one of the operators.
4. And even when I try to work around the functional I have problems:

>>> f1 = (L2NormSquared.translated(someElement)) * forwardOp
>>> f = odl.ReductionOperator(f1, 2)

throws an error since f1 is of type FunctionalComp and 'operators' must be a matrix of 'Operator' objects, '0' or 'None' (<= by the way: shouldn't this rather read ... must be an array/list of ...?)
5. But most importantly: Thank you for this nice module!

@aringh
Copy link
Member

aringh commented Apr 26, 2018

Hi @miallo! You are bumping into an age-old discussion/issue, namely #590.

Yes, a norm should map to the real numbers. However, the definition of a functional that we have used (at least so far) in odl is that it is a mapping from a vector space into the field of scalars in that space (see, e.g., https://en.wikipedia.org/wiki/Functional_(mathematics)). However, it is this that has caused all the headache you now encounter.

Also: great that you like odl ^_^

@miallo
Copy link
Contributor Author

miallo commented Apr 26, 2018

Thank you! I see... I must have been stuck in the "historical" point of view... 😁

Can you give me a hint then on how to extract the real part out of the ComplexNumbers()? Since I use the operator I cant just type L2NormSquared.real and as I mentioned in 3. I couldn't get odl.RealPart working as well...

Now the stupid physicist inside of me asks: Would something break if you set the range of a Functional by default to space.field and if space.field != range set linear=False?

@miallo miallo changed the title Why is L2Norm.range==ComplexNumbers? Is it reasonable to change the implementation of Functional.range? Apr 26, 2018
@kohr-h
Copy link
Member

kohr-h commented Apr 26, 2018

Hey @miallo, many thanks for your feedback! Adding to @aringh's comments, our support for complex functionals isn't terribly well-established. However, most of the issues have to do with complex-valued functionals, where it's not obvious how to define convex conjugate, proximal etc.

There are also some related issues with real<->complex mappings, some of which are discussed in #1317 and #1324. The approach there is basically what you suggest (so it's not stupid at all), only for Operators, not Functionals.

However in your case of complex->real functionals we might actually get around those issues for derived properties of such functionals. Let me try to think aloud:

  • The gradient operator is mapping between complex spaces, so no problem there.
  • The derivative operator uses the default implementation derivative(x) = gradient.T(x). However, the derivative should map complex->real like the original functional, so we would need to take the real part of the above result. Hence, the default must be overridden.
  • I don't see issues with convex_conj or proximal, although the devil may be in the detail (or in the default implementations :-) ).
  • It would make sense for RealNumbers and ComplexNumbers to implement real_space and complex_space just as linear spaces. They already have some functionality that make them look like spaces, so these two should be added.
    That way you could use the construction RealPart(ComplexNumbers()) * gradient(x).T as you suggested.

The best way forward from my perspective would be to use domain and range=None in the Functional.__init__ method, and set range to domain.field if None. If renaming space to domain would break any code, that code would need to be fixed. Then you can just go ahead and pass RealNumbers() as range instead.

And, of course, nice that you like ODL 😃

@miallo
Copy link
Contributor Author

miallo commented Apr 26, 2018

Thank you both for your help! It might take me some time to get this done properly, but at least now I know what to do 😃

@miallo miallo closed this as completed Apr 26, 2018
@kohr-h
Copy link
Member

kohr-h commented Apr 26, 2018

Since this is a desirable change, I'll reopen this so we can track the underlying issue. If you want to make a PR, @miallo, we'd highly appreciate that. You can make it a WIP (work-in-progress) PR if you want some early feedback.

@kohr-h kohr-h reopened this Apr 26, 2018
@kohr-h kohr-h changed the title Is it reasonable to change the implementation of Functional.range? Implement complex -> real functionals (e.g. L2Norm) Apr 26, 2018
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

3 participants