-
-
Notifications
You must be signed in to change notification settings - Fork 190
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
Replace sqrt(dot_self()) with norm2 #2648
Conversation
Jenkins Console Log Machine informationProductName: Mac OS X ProductVersion: 10.11.6 BuildVersion: 15G22010CPU: G++: Clang: |
Thanks for the PR! I'm not sure whether this change is actually necessary though, as it looks like this essentially just replaces |
That's fair -- I was thinking that too as I was making the changes. Perhaps there was something else @bob-carpenter had in mind in the issue #2562 . |
@andrjohns: We want to replace |
Right. I replaced instances of |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmm. These changes are OK semantically, but I think the original versions are slightly faster in all cases because it's not just the norm2 calculation, we also need its square.
If you want to work on speed here, what we could use is the autodiff implemented analytically here like for any other special function. The twist is that we also need to set lp
(right now it just does lp +=
, but we should set it to a new var
that holds a pointer to lp
and to y
), and also define the version that doesn't increment lp
.
What we'd really like instead of this replacement is analytic gradients for the constraint functions. We should be able to define this just like any other autodiffable function but have it also set lp
to a new value.
eig_partial squared_norm = dot_self(y_val); | ||
eig_partial norm = sqrt(squared_norm); | ||
eig_partial norm = norm2(y_val); | ||
eig_partial squared_norm = norm * norm; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These are going to be very similar operationally. I think the original code may require fewer operations and be more stable arithmetically, because the new one has the sqrt inside norm2, and then you have to the do the additional squaring. So unless there's something I'm not seeing, I think this should be switched back. If for some reason you do want to keep the new code, line 27 should be square(norm)
.
@@ -34,7 +34,7 @@ inline auto unit_vector_constrain(const T& y) { | |||
arena_t<T> arena_y = y; | |||
arena_t<promote_scalar_t<double, T>> arena_y_val = arena_y.val(); | |||
|
|||
const double r = arena_y_val.norm(); | |||
const double r = norm2(arena_y_val); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For double
arguments, it's probably better to use the built-in Eigen functions as they're more highly optimized for this. Of course, norm2
should delegate to this .norm()
for values in the vector case.
return y_ref.array() / sqrt(SN); | ||
value_type_t<T1> r = norm2(y_ref); | ||
check_positive_finite("unit_vector_constrain", "norm", r); | ||
lp -= 0.5 * (r * r); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This looks like it's also just paying that squaring cost again here on line 51, so I think the original code may again be faster here.
What we really want here is a specialized function for unit_vector_constrain that does all of this with analytic derivatives for lp and the return type.
Thanks for the review @bob-carpenter. It looks like this is a bit more nuanced than initially anticipated. I think I'll close the PR for now until we settle on something that requires more significant change. |
Summary
I'm hoping this one won't be too controversial. This PR addresses the remaining TODO in #2562.
PR #2636 introduced specialized prim/fwd/rev functions for L1 and L2 norms. This PR tidies some instances in the codebase where
r = sqrt(dot_self(x));
can now be replaced withr = norm2(x);
. The main ones I found were:unit_vector_constrain
in prim/fwd/revIf there are any instances I may have missed, please let me know.
Tests
No new tests. Existing tests pass.
Side Effects
N/A
Release notes
Replace this text with a short note on what will change if this pull request is merged in which case this will be included in the release notes.
Checklist
Math issue L1 and L2 norms (Euclidean and taxicab lengths) #2562
Copyright holder: Lyndon Duong
The copyright holder is typically you or your assignee, such as a university or company. By submitting this pull request, the copyright holder is agreeing to the license the submitted work under the following licenses:
- Code: BSD 3-clause (https://opensource.org/licenses/BSD-3-Clause)
- Documentation: CC-BY 4.0 (https://creativecommons.org/licenses/by/4.0/)
the basic tests are passing
./runTests.py test/unit
)make test-headers
)make test-math-dependencies
)make doxygen
)make cpplint
)the code is written in idiomatic C++ and changes are documented in the doxygen
the new changes are tested