-
-
Notifications
You must be signed in to change notification settings - Fork 10.9k
corrcoef started to escape [-1, 1] range #7392
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
Comments
bisect lead to following commit (08e8c14) which I haven't double checked but commit msg suggests that it must be the one ;)
|
I note that the error is 1 ulp, I wonder if that is worth worrying about, roundoff error is to be expected in floating point computations. |
IMHO it is not just a precision issue. First of all it is a regression and IMHO should be treated as such -- hard to say how many people are possibly relying on the fact that corrcoef <= 1.0, which wouldn't be the case here regardless how small imprecision is. |
@yarikoptic: that seems like a fair point. I guess it's sort of similar to how a 1 eps error that changes the sign is really bad (even though 1 eps errors are usually OK) -- preserving structural invariants is different than plain accuracy. Do you see an easy way to fix this? (I guess we could just call Cc @rudimeier |
to me nothing else than straightforward as clipping came to mind, but I didn't look yet into the OPT reimplementation which lead to this situation |
The issue seems to come from when the sqrt is computed. The new code (08e8c14) computes the outer product of the square root of d with itself. The old code (e6dbcea) computed the square root of the outer product of d with itself. In particular, if we change the old code as follows:
then the sample problem occurs:
A possible fix would be to change the new code to compute the sqrt in the loop
However, this slows down the execution. Without the proposed change:
With the proposed change:
For reference, here's the performance before the bug was introduced:
Hope this helps. I'll see if I can find a fix that doesn't affect performance. |
On Friday 11 March 2016 11:05:16 frederikhermans wrote:
IMO you could never proof that any algorithm on certain machines would never So why not simply something like this at the end: |
If it ends up boiling down to @rudimeier's post processing suggestion, remember |
I agree with @rudimeier that moving the My only concern with |
@frederikhermans Good point. I'd go further and argue that any program that relied on the inequality being exactly preserved is likely incorrect. |
Note that the current implementation still uses too much memory ;)
could be replaced by
and |
And both of the improved methods can loose symmetry when |
This seems unnecessarily pessimistic. Floating point is quirky, but it isn't random; IEEE 754 is very carefully designed to make it possible to write algorithms that have various nice numerical guarantees, even when using floating point. (At least, if you are very clever.) But yeah, I'm not 100% sure but I do suspect it's true that the old algorithm could also produce values outside [0, 1]. Apparently not as often, though? I'd still like to find whatever literature there is on this (anyone know a numerical analyst?), because we are certainly not the first to run into it.
I dunno. Would you feel the same way about a program that relied on real-valued sqrt returning a number >= 0? (E.g., does code like |
On Friday 11 March 2016 17:32:19 Charles Harris wrote:
Of course, but clip to [-1,1] would not hurt anybody. On Friday 11 March 2016 17:43:28 Charles Harris wrote:
I feel guilty because in pull request #6396 I had promised to add blas syrk()
This was discussed already in pull request #6396, see On Friday 11 March 2016 17:53:53 Charles Harris wrote:
This should be fixed by the way when we use syrk(). |
Different case, and sign is exact. The use of positive square roots instead of negative is a choice and that is specified. OTOH, it wouldn't surprise me if an algorithm that assumed that a computed number was non-negative and took the square root of the result might lead to an exception. |
Sorry my last reply by email was messed up somehow. I think this numpy issue tracker behave differently than github default ..., here again:
Of course, but clip to [-1,1] would not hurt anybody.
I feel guilty because in pull request #6396 I had promised to add blas syrk()
This was discussed already in pull request #6396, see
This should be fixed by the way when we use syrk(). |
That applies to the unnormalized correlation. The normalization also needs to preserve symmetry, which is not the case in the improvement I gave as the divisions take place in different order for symmetric off axis elements. Probably OK with current implementation if multiplication is commutative. |
I think the function Is busted anyway, as diag is used and it returns a view these days. I've had my doubts that making diagonal return a view was a good idea, those doubts are getting stronger... EDIT: And we are doing an inplace operation on the array from which the diagonal is taken. |
In any case, we should make sure that inplace operations work with overlapping lhs and rhs befor going to far down the view route. |
After thinking a bit and failing to find anything useful in Google, I'm leaning towards using something like
(Or the |
I can even see an argument for doing |
i think you mean |
Sorry yes, -1 obviously. And yeah, that's why the |
I have tested something like this: d = 1.0/sqrt(d)
n = d.size
for i in range(0, n):
c[i,i+1:n] *= (d[i+1:n] * d[i])
np.clip( c[i,i+1:n], -1, 1, out=c[i,i+1:n])
c[i+1:n,i] = c[i,i+1:n].conj()
c[i,i] = 1.0 It's symmetric, In range [-1,1] and diag = 1.0. Would there be a chance to get this merged if I implement that in C? I expect a speed-up by factor 2. |
@rudimeier I wouldn't be a fan of a C implementation. IMHO, even clipping verges on overkill, floating point is floating point, and there is virtue in having simple and transparent code. |
@njsmith I don't think using the inverse is quite the same as using a matrix inverse, in particular, no row pivoting. The advantage of doing multiplication is that it is faster than division. |
Note that clipping doesn't work for complex values. The Schwarz inequality only gives us that abs(x) <= 1 for the elements x of the result. Consequently we would need to clip the modulus of complex values, and I don't see a roundoff safe way to do that. |
For complex observations we should use the real part of the diagonal, and probably just replace the diagonal by 1. The is a limit to what we can do with off diagonal elements. |
On Sat, 12 Mar 2016, Charles Harris wrote:
I know that this is a somewhat awkward but I think a viable usecase |
Note that NaN is a valid result, so we cannot just fill the diagonal with ones. I suggest if we want to make a stab at maintaining abs(c[i,j]) <= 1, that we simply clip the real and imag parts to the interval [-1, 1]. That doesn't cure all problems but is the only thing that is simple and easy to understand. Myself, I'd be inclined to just leave everything alone... |
PR for consideration in #7414. |
The non-nan elements of the result of corrcoef should satisfy the inequality abs(x) <= 1 and the non-nan elements of the diagonal should be exactly one. We can't guarantee those results due to roundoff, but clipping the real and imaginary parts to the interval [-1, 1] improves things to a small degree. Closes numpy#7392.
The non-nan elements of the result of corrcoef should satisfy the inequality abs(x) <= 1 and the non-nan elements of the diagonal should be exactly one. We can't guarantee those results due to roundoff, but clipping the real and imaginary parts to the interval [-1, 1] improves things to a small degree. Closes numpy#7392.
Our tests of PyMVPA started to fail while building in Debian sid which carries beta of 1.11, so tried now on master:
The text was updated successfully, but these errors were encountered: