-
-
Notifications
You must be signed in to change notification settings - Fork 30.3k
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
gh-117999: drop special case for small integer exponents in complex_pow() #123283
base: main
Are you sure you want to change the base?
Conversation
I had some thoughts on that matter. For small integer exponents vabs = hypot(a.real,a.imag);
len = pow(vabs,b.real);
at = atan2(a.imag, a.real);
phase = at*b.real;
/* if (b.imag != 0.0) {
len /= exp(at*b.imag);
phase += b.imag*log(vabs);
} */ // ignored since b.imag == 0
r.real = len*cos(phase);
r.imag = len*sin(phase); Let I don't know whether the costly computation is due to |
Yeah, below are tests copied from #118000 (comment) with few more with odd exponents: With specialized code (main):
Without:
|
Can I have the numbers for odd exponents without as well please? |
Sure, comment was updated. As you can see, results aren't biased to even/odd integers. |
Do you think it's worth checking the even case for a fast path or do you think we should just live with those this loss of performance? (personally, in this case, I'm for correctness rather than speed). |
I think it not worth. Certainly, this can't restore speed for small integers (for even case). |
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.
I'm sorry but I approved without commenting ...
self.assertComplexesAreIdentical(complex(1, +0.0)**2, complex(1, +0.0)) | ||
self.assertComplexesAreIdentical(complex(1, -0.0)**2, complex(1, -0.0)) |
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.
You could actually have more test cases if you want?
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.
Yes, that's why I also referenced #60200. We can include more test cases in some new file of Lib/test/mathdata/. But I'm not sure this belongs to the current pr.
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.
I like having extensible test coverage so I would be in favor of adding more cases in this PR since we are removing a functionality. But let's leave this decision to the core dev merging it.
Misc/NEWS.d/next/Library/2024-08-24-10-46-35.gh-issue-117999.5K_BiA.rst
Outdated
Show resolved
Hide resolved
Co-authored-by: Bénédikt Tran <[email protected]>
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.
As I said, I'm in favor of correctness rather than speed in this case. But since #60200 is still under discussion, I'll be interested in hearing from @mdickinson and @serhiy-storchaka for this one.
self.assertComplexesAreIdentical(complex(1, +0.0)**2, complex(1, +0.0)) | ||
self.assertComplexesAreIdentical(complex(1, -0.0)**2, complex(1, -0.0)) |
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.
I like having extensible test coverage so I would be in favor of adding more cases in this PR since we are removing a functionality. But let's leave this decision to the core dev merging it.
Can you update your PR? There is now a conflict (with the ERANGE fix). |
So Python has its own cpow() implementation: Py_complex
_Py_c_pow(Py_complex a, Py_complex b)
{
Py_complex r;
double vabs,len,at,phase;
if (b.real == 0. && b.imag == 0.) {
r.real = 1.;
r.imag = 0.;
}
else if (a.real == 0. && a.imag == 0.) {
if (b.imag != 0. || b.real < 0.)
errno = EDOM;
r.real = 0.;
r.imag = 0.;
}
else {
vabs = hypot(a.real,a.imag);
len = pow(vabs,b.real);
at = atan2(a.imag, a.real);
phase = at*b.real;
if (b.imag != 0.0) {
len /= exp(at*b.imag);
phase += b.imag*log(vabs);
}
r.real = len*cos(phase);
r.imag = len*sin(phase);
_Py_ADJUST_ERANGE2(r.real, r.imag);
}
return r;
} Can we reuse the cpow() of the C library instead? A C11 compiler is now required to build CPython: https://docs.python.org/dev/using/configure.html#build-requirements |
Done.
Yet we don't require optional C11 features, cpow() - is from Annex G. Not even all compilers implement one (mvsc for example, though it has something similar). And I'm not sure about quality of libm implementations for that part of the standard. After support for C complex in ctypes was merged, I did tests (same as in CMathTests.test_specific_values) of cmath_testcases.txt on top of Debian's 12 default gcc & glibc: to my surprise it was working (modulo two simple failures from changes in C17, now fixed). But perhaps I'm just lucky Linux user. |
I'm not sure that I understood correctly the purpose of this change. Is it supposed to fix some corner cases and enhance correctness? The change only adds 2 tests which is small. |
Try |
Without the change, I get:
With the change, I get:
|
Well, yes. See examples in the issue description: >>> z = complex(1,-0.0)
>>> z**2
(1+0j)
>>> z*z
(1-0j)
>>> z**2000 # large powers or non-integer - have own opinion
(1-0j)
>>> >>> z**2.1
(1-0j)
>>> _testcapi._py_c_pow(z, 2)[0] # as well as _Py_c_pow() algorithm
(1-0j)
Agreed. I'll work on this.
That's something, which now is more precise in the main, as an accident. But: >>> _testcapi._py_c_pow(z, 1)[0]
(-1+1.2246467991473532e-16j)
>>> libm.cpow(z, 1) # libm's cpow
(-1+1.2246467991473532e-16j) |
See also #124243. |
This has hegative impact on performance, see #118000 (comment). On another hand, it fixes issue and makes CPython pow() behave like cpow() from the C99 standard. Glibc, for example, has no special code to handle small integer exponents: https://sourceware.org/git/?p=glibc.git;a=blob;f=math/s_cpow_template.c;h=92e3e986639be118ec36fb7f991ba47d8115816e;hb=HEAD Ditto GSL: https://git.savannah.gnu.org/cgit/gsl.git/tree/complex/math.c#n398