-
Notifications
You must be signed in to change notification settings - Fork 56
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
DDtheta: numerical stability of small angular separation in float32 #296
Comments
At first I thought this might be due to @manodeep I think the method we're using to compute delta-theta is not numerically stable. We're doing DOUBLE costheta = (one - half*sqr_chord_sep); where Perhaps we could switch to Or perhaps we could switch to the small angle approximation Or perhaps there's another trig trick that avoids |
Thanks @JonLoveday for reporting the issue! It's not entirely unexpected - computing with floating points numbers can cause such seemingly odd behaviour. Corrfunc was not designed to be resilient against such numerical issues - for example, What I would also be curious to know is if a brute-force python computation with float-precision produced results more consistent with double-precision. @lgarrison Yeah |
Thanks @lgarrison and @manodeep! In the past, I have always used theta = 2*arcsin(C/2). It just occurred to me that you can avoid both the arcsin and sqrt calculations by converting the bins from theta -> 4 sin^2(theta/2). |
Currently Corrfunc can compute angular separations between [0, 180] - i.e., |
Right! |
Thanks for the great suggestion, @JonLoveday! I made a proof-of-concept in #297. The biggest hurdle remaining is probably deciding how to compute theta for purposes of returning the average binned theta. We could bite the bullet and do On the other hand, whatever we choose has to be implemented in the SIMD kernels, which makes a theta computation with two branches less appealing. I've updated the title to reflect the underlying issue. |
I think for most practical purposes, it would be good enough to calculate the average binned C^2 value, and then transform to an average theta. |
While looking at #301, I realised that we could easily recast the current calculation to be: |
I think this issue is less about avoiding the |
I thought that the issue was arising because 1 - 0.5*C^2 for small C was numerically unstable, and switching to the dot product would add terms that were all of similar magnitude, and hence better-behaved numerically. @lgarrison I can confirm that your assertion is correct - even with the dot-product method (in #303), the second bin still contains 0 elements for a |
Modifying the reproducer slightly to check the bins: import numpy as np
def corrfunc_test(nran=10000, tmin=0.01, tmax=10, nbins=20):
"""Random-random pair count test."""
bins = np.geomspace(tmin, tmax, nbins + 1)
for typ in [np.float32, np.float64]:
cosbins = np.cos(bins*np.pi/180., dtype=typ)
diff = np.diff(cosbins)
print("float type = {} max. diff = {}".format(typ, max(diff))) produces float type = <class 'numpy.float32'> max. diff = 0.0
float type = <class 'numpy.float64'> max. diff = -1.5158711841323225e-08 |
I think using sep^2 as the variable (i.e. #297) is generally a better method, since we don't care as much about precision at large angles. I don't even think we'd need to switch between variables. However, for computing thetaavg, different methods are probably better for different separations (#296 (comment)), which is annoying for SIMD kernels. And the warnings in #299 are all tunable; I chose a 1% loss of precision in theta as the threshold, but this can be changed or promoted to an error as desired. |
@lgarrison I don't quite follow why . The 0-counts issue that @JonLoveday has reported is occurring because the second bin has zero width when Separately, there is the issue of what's the best numerical method to use with 32-bit floats. To me, the calculation of Regardless of the method, we will need an inverse trig call when computing theta-averages - so I am not sure whether the timings will differ too much between |
General information
Issue description
When I perform pair counts on a random distribution with float32 precision using Corrfunc.mocks.DDtheta_mocks with 20 log bins between 0.01 and 10 degrees, I always get zero pairs in the second bin. Using float64 alleviates this problem. I'm not sure if this is a bug as such, but users should be strongly advised to only use float64 coordinates.
Expected behavior
Would not expect significantly different results betweein flloat32 and float64 coords for angular precision > 0.01 deg.
Actual behavior
Zero pair counts in second bin using float32.
What have you tried so far?
Using float64 precision avoids this problem. In fact, pair counts in all bins change.
Minimal failing example
The text was updated successfully, but these errors were encountered: