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

API: Changed FunctionalLinearPertub to quadratic #1066

Merged
merged 3 commits into from
Jul 10, 2017

Conversation

adler-j
Copy link
Member

@adler-j adler-j commented Jul 7, 2017

I guess we could use some more tests but this is very close to done.

dual = simple_fixture('dual', [False, True])


func_params = ['l1', 'l2', 'l2^2', 'kl', 'kl_cross_ent', 'const',
'groupl1-1', 'groupl1-2',
'nuclearnorm-1-1', 'nuclearnorm-1-2', 'nuclearnorm-1-inf'
Copy link
Member Author

Choose a reason for hiding this comment

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

That this bug has been there for quite some time is scary. How often do we run largescale tests?

@adler-j
Copy link
Member Author

adler-j commented Jul 8, 2017

This should now be feature complete, since @matthiasje suggested this change, perhaps you could help out with a quick review, especially of the features?

return self.functional.convex_conj.translated(
self.linear_term)
else:
return super().convex_conj
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 guess this should be changed to python 2 style super calls

Copy link
Member

Choose a reason for hiding this comment

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

Would be reasonable I think.

Copy link
Member

@kohr-h kohr-h left a comment

Choose a reason for hiding this comment

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

I have one general comment about the types of tests that I think we need to avoid, namely tests that more or less just repeat the implementation.

Other than that, it's just some doc things, otherwise good to go.

"""Initialize a new instance.

Parameters
----------
func : `Functional`
Function corresponding to ``f``.
linear_term : `domain` element
quadratic_term : ``domain.field`` element, optional
Copy link
Member

Choose a reason for hiding this comment

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

"Term" in what sense? A quadratic term should be quadratic, this is just a scalar constant.

Copy link
Member Author

Choose a reason for hiding this comment

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

Any better suggestion? In math I'd just call this something like a, but that clearly won't work here.

Copy link
Member

Choose a reason for hiding this comment

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

How about quadratic_coeff? It's a polynomial after all.

Copy link
Member

Choose a reason for hiding this comment

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

In some sense, the linear_term (name also not ideal) is also just a vector of coefficients, at least in the discrete case. Otherwise it's more of a factor than a term.

Copy link
Member Author

Choose a reason for hiding this comment

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

quadratic_coeff is a great idea! I'll give the linear one some thought.


"""The ``Functional`` representing ``f(.) + <linear_term, .>``."""
"""The ``Functional`` representing ``F(.) + a * |.|^2 + <., u>``."""
Copy link
Member

Choose a reason for hiding this comment

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

I'd write replace ``Functional`` by `Functional` but these links don't work in the one-line summaries, so just go for "functional"

Copy link
Member

Choose a reason for hiding this comment

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

The norm could be anything, I'd prefer a * <., .> + <., u>.

"""Initialize a new instance.

Parameters
----------
func : `Functional`
Function corresponding to ``f``.
linear_term : `domain` element
quadratic_term : ``domain.field`` element, optional
Quadratic term.
Copy link
Member

Choose a reason for hiding this comment

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

Mention nonnegativity requirement.

Copy link
Member Author

Choose a reason for hiding this comment

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

Second thought, why do we need that. Its only a requirement for the proximal. I'll remove the requirement.

if linear_term is not None:
self.__linear_term = func.domain.element(linear_term)
else:
self.__linear_term = func.domain.zero()
Copy link
Member

Choose a reason for hiding this comment

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

This seems like a waste of space for questionable gain in _call.
And it won't work for domains that are not linear spaces.

Copy link
Member Author

@adler-j adler-j Jul 9, 2017

Choose a reason for hiding this comment

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

Main idea here is that users should be able to simply call FunctionalQuadraticPerturb(func, quadratic_term=3.2) without mentioning the linear term.

Edit:

I see that you want me to move the logic into _call, adjoint, etc. I'll probably leave that out for now untill we see if anyone uses this function enough that such stuff will be noticeable for performance.

Copy link
Member

Choose a reason for hiding this comment

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

Fair enough, but there's still the issue that the functional doesn't work with e.g. RealNumbers like that.

Copy link
Member Author

Choose a reason for hiding this comment

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

RealNumbers does not impement inner, currently at least, so that ain't much of an issue for now.

Copy link
Member

Choose a reason for hiding this comment

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

But still weird to let this guy raise an error for the default in that case.

self.__linear_term = func.domain.element(linear_term)
else:
self.__linear_term = func.domain.zero()

# Only compute the grad_lipschitz if it is not inf
if (not func.grad_lipschitz == np.inf and
Copy link
Member

Choose a reason for hiding this comment

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

if func.grad_lipschitz != np.inf


@property
def functional(self):
"""The original functional."""
return self.__functional

@property
def quadratic_term(self):
"""The translation."""
Copy link
Member

Choose a reason for hiding this comment

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

Copy-paste

return self.functional.convex_conj.translated(
self.linear_term)
else:
return super().convex_conj
Copy link
Member

Choose a reason for hiding this comment

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

Would be reasonable I think.


.. math::
\mathrm{prox}_{\\sigma F( \cdot scal)}(x) =
\\frac{1}{scal} \mathrm{prox}_{\\sigma \, scal^2 \, F }(x \, scal)
\mathrm{prox}_{\\sigma F( \cdot \\alpha)}(x) =
Copy link
Member

Choose a reason for hiding this comment

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

\\alpha \cdot

\\frac{1}{scal} \mathrm{prox}_{\\sigma \, scal^2 \, F }(x \, scal)
\mathrm{prox}_{\\sigma F( \cdot \\alpha)}(x) =
\\frac{1}{\\alpha}
\mathrm{prox}_{\\sigma \, \\alpha^2 \, F }(x \, \\alpha)
Copy link
Member

Choose a reason for hiding this comment

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

\\alpha x

assert all_almost_equal(functional(x),
(orig_func(x) +
quadratic_term * x.inner(x) +
x.inner(linear_term)),
Copy link
Member

Choose a reason for hiding this comment

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

I'm honestly not so sure about these kinds of tests. What I mean by "these tests" is tests of the implementation of something rather than the intended functionality of something.

What I think one should do instead (and this is not easy while implementing stuff) is to take a step back, ask oneself "What is this function supposed to do?" and then test that. Or test things that implicitly follow but aren't directly implemented.
In this case it's almost the same, but there are still some differences, for instance what happens with weighted spaces. Errors there won't be caught by this test.

Here I think it's better to test an explicit example, and when we feel we need to settle on some behavior regarding weighting (it will influence the gradient) we add a test for that.

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'm honestly not so sure about these kinds of tests. What I mean by "these tests" is tests of the implementation of something rather than the intended functionality of something.

Well (imo) these dont test anything right now, but are only there as regression tests to give us a huge red flag if something gets changed so we actually notice. I don't really see any issue with that. They also make sure the code is run at least once which stops the worst of bugs.

With that said, you are certainly correct in that this could be done in a better manner, but that would require quite some effort here, something I probably wouldn't feel is warranted for a function that is as specialized as this once.

In other places, like Gradient which is called all the time, I certainly agree and a proper look at those tests is sorely needed.

Copy link
Member

Choose a reason for hiding this comment

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

I agree that it would be quite a bit of work and maybe not something to do right now. When writing new tests, however, we should keep this in mind and write "proper" tests right from the beginning.

@adler-j adler-j force-pushed the issue-1064__quadratic_pertubation branch from 37978c4 to d0c3aac Compare July 10, 2017 13:11
@adler-j adler-j merged commit bab08e8 into odlgroup:master Jul 10, 2017
@adler-j adler-j deleted the issue-1064__quadratic_pertubation branch July 10, 2017 13:28
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