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

gh-69639: add mixed-mode rules for complex arithmetic (C-like) #124829

Open
wants to merge 24 commits into
base: main
Choose a base branch
from

Conversation

skirpichev
Copy link
Member

@skirpichev skirpichev commented Oct 1, 2024

"Generally, mixed-mode arithmetic combining real and complex variables should be performed directly, not by first coercing the real to complex, lest the sign of zero be rendered uninformative; the same goes for combinations of pure imaginary quantities with complex variables." (c) Kahan, W: Branch cuts for complex elementary functions.

This patch implements mixed-mode arithmetic rules, combining real and complex variables as specified by C standards since C99. Most C compilers implementing C99+ Annex G have only these special rules (without support for imaginary type, which is going to be deprecated in C2y).


With this patch:

>>> 2-0j  # was (2+0j)
(2-0j)
>>> z = complex(-0.0, 2)
>>> cmath.asinh(z)
(-1.3169578969248166+1.5707963267948966j)
>>> cmath.log(z + cmath.sqrt(1 + z*z))  # real component had wrong sign before
(-1.3169578969248164+1.5707963267948966j)
>>> (cmath.inf+1j)*2  # imaginary component was nan before
(inf+2j)

So, new rules allow to use complex arithmetic for implementation of mathematical functions in more cases.

Notes for reviewers


📚 Documentation preview 📚: https://cpython-previews--124829.org.readthedocs.build/

"Generally, mixed-mode arithmetic combining real and complex variables should
be performed directly, not by first coercing the real to complex, lest the sign
of zero be rendered uninformative; the same goes for combinations of pure
imaginary quantities with complex variables." (c) Kahan, W: Branch cuts for
complex elementary functions.

This patch implements mixed-mode arithmetic rules, combining real and
complex variables as specified by C standards since C99 (in particular,
there is no special version for the true division with real lhs
operand).  Most C compilers implementing C99+ Annex G have only these
special rules (without support for imaginary type, which is going to be
deprecated in C2y).
Objects/complexobject.c Outdated Show resolved Hide resolved
Objects/complexobject.c Outdated Show resolved Hide resolved
@skirpichev

This comment was marked as resolved.

@ericsnowcurrently ericsnowcurrently removed their request for review October 1, 2024 16:04
Copy link
Member

@serhiy-storchaka serhiy-storchaka left a comment

Choose a reason for hiding this comment

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

LGTM in general. Very LGTM! I only left a few suggestions for style.

Objects/floatobject.c Outdated Show resolved Hide resolved
Objects/complexobject.c Outdated Show resolved Hide resolved
Objects/complexobject.c Outdated Show resolved Hide resolved
Objects/complexobject.c Outdated Show resolved Hide resolved
Lib/test/test_complex.py Outdated Show resolved Hide resolved
* more tests for multiplication
* rename to _Py_convert_int_to_double
* rename to real_to_float/complex
* slightly optimize code
@skirpichev
Copy link
Member Author

@serhiy-storchaka, what do you think on fixing repr(complex(-0.0, 1)) or adding special case for the true division (this is what you did in #124243 or I did in skirpichev#1)?

I also worry that this is a half-way solution: this neither fixes all eval(repr) round-trip issues or allows to use any analytical identity from textbook, e.g.:

>>> -0.0+1j
1j
>>> z = complex(-0.0, 2)
>>> 1j*(cmath.log(1 - 1j*z) - cmath.log(1 + 1j*z))/2
(1.5707963267948966+0.5493061443340549j)
>>> cmath.atan(z)
(-1.5707963267948966+0.5493061443340549j)

Proper fix seems to be only something like skirpichev#1. Does it look complicated for you? (Note, that arithmetic methods on the complex type are mostly unchanged wrt the current pr.)

@skirpichev
Copy link
Member Author

I also was thinking about using additional C-API helper functions like _Py_c_sum_real() (as GSL does), instead of inlining that stuff. Does it make sense for you?

Objects/complexobject.c Outdated Show resolved Hide resolved
@serhiy-storchaka
Copy link
Member

I think that the idea of special handling of mixed complex-real arithmetic is much easier to "sell" than the idea of the imaginary number class. And it solves a half of problems. It will help to convince in necessarily to support pure imaginaries. Let's eat the elephant piece by piece.

adding special case for the true division

I was surprised you did not include it. What does C99+ say about it? It looks natural and is useful in some cases, so I would implement it even if the C standard omits it.

I also was thinking about using additional C-API helper functions like _Py_c_sum_real() (as GSL does), instead of inlining that stuff. Does it make sense for you?

It makes sense to me. The status of functions like _Py_c_sum() is unclear -- they are private, but documented. Some of them are used outside of complexobject.c, and some of them are non-trivial, -- this is likely the reason of exposing them. I think that this all would also be true for the new functions.

Of course, the changes in arithmetic and the new C API (even if it is private) should be documented in many places.

@skirpichev
Copy link
Member Author

And it solves a half of problems.

And that is a problem.

I was surprised you did not include it. What does C99+ say about it?

It seems, there is no such special version in the C standard. Not sure why. And such case miss in implementations, e.g. clang:
https://github.com/llvm/llvm-project/blob/496187b3b81ea76a8b67d796609d7f09992cf96d/libcxx/include/complex#L835-L841

On another hand, GSL or MPC libraries implement such case.

I would implement it even if the C standard omits it.

Ok, I'll add this with naming scheme like in your pr.

Of course, the changes in arithmetic and the new C API (even if it is private) should be documented in many places.

New arithmetic rules documented in stdtypes.rst, C API stuff will go to Doc/c-api/complex.rst. Did I miss something else?

@skirpichev skirpichev marked this pull request as draft October 2, 2024 07:47
@serhiy-storchaka
Copy link
Member

New arithmetic rules documented in stdtypes.rst, C API stuff will go to Doc/c-api/complex.rst. Did I miss something else?

In Doc/reference/expressions.rst: "If either argument is a complex number, the other is converted to complex". There may be other leftovers.

You missed a What's New entry and versionchanged directives.

@skirpichev skirpichev marked this pull request as ready for review October 2, 2024 11:46
Co-authored-by: Bénédikt Tran <10796600+picnixz@users.noreply.github.com>
Doc/c-api/complex.rst Outdated Show resolved Hide resolved
Objects/complexobject.c Show resolved Hide resolved
Co-authored-by: Bénédikt Tran <10796600+picnixz@users.noreply.github.com>
@picnixz picnixz self-requested a review October 13, 2024 15:31
picnixz
picnixz previously approved these changes Oct 13, 2024
Copy link
Contributor

@picnixz picnixz left a comment

Choose a reason for hiding this comment

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

Looks good to me!

Co-authored-by: Bénédikt Tran <10796600+picnixz@users.noreply.github.com>
Copy link
Contributor

@willingc willingc left a comment

Choose a reason for hiding this comment

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

This looks well documented and the C code is clear. Thanks @skirpichev.

Objects/complexobject.c Outdated Show resolved Hide resolved
Objects/complexobject.c Outdated Show resolved Hide resolved
Objects/complexobject.c Show resolved Hide resolved
Objects/complexobject.c Outdated Show resolved Hide resolved
Copy link
Contributor

@picnixz picnixz left a comment

Choose a reason for hiding this comment

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

Some questions I suddenly had (sorry for this!)

Objects/complexobject.c Outdated Show resolved Hide resolved
Objects/complexobject.c Outdated Show resolved Hide resolved
Objects/complexobject.c Outdated Show resolved Hide resolved
Objects/complexobject.c Outdated Show resolved Hide resolved
Objects/complexobject.c Outdated Show resolved Hide resolved
Objects/complexobject.c Outdated Show resolved Hide resolved
Copy link
Contributor

@picnixz picnixz left a comment

Choose a reason for hiding this comment

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

One last missing modification and LGTM! Thanks for your patience Sergey!

Objects/complexobject.c Outdated Show resolved Hide resolved
Co-authored-by: Bénédikt Tran <10796600+picnixz@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants