-
-
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
Invalid "equivalents" of the complex type constructor in docs #109218
Comments
See also https://discuss.python.org/t/33433/ A |
Note that the two arguments to
|
Indeed, but in this form the "equivalency" >>> complex(r := nan-infj, i := -inf+infj)
(nan-infj)
>>> r + i*1j
(nan+nanj)
>>> complex(r := -0.0-0j, i := 1+0j)
(-0+1j)
>>> r + i*1j
1j Under the hood, instead, we have: >>> complex(r.real - i.imag, i.real + r.imag)
(-0+1j)
It's used in the current docstring, anyway. Lets decide, however, whether this is a bug, regardless from the outcome in the mentioned discussion. Or not (as for |
…hmetic "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. That's why C standards since C99 introduce imaginary types. This patch proposes similar extension to the Python language: * Added a new subtype (imaginary) of the complex type. New type has few overloaded methods (conjugate() and __getnewargs__()). * Complex and imaginary types implement IEC 60559-compatible complex arithmetic (as specified by C11 Annex G). * Imaginary literals now produce instances of imaginary type. * cmath.infj/nanj were changed to be of imaginary type. * Modules ast, code, copy, marshal got support for imaginary type. * Few tests adapted to use complex, instead of imaginary literals - Lib/test/test_fractions.py - Lib/test/test_socket.py - Lib/test/test_str.py Lets consider some (actually interrelated) problems, shown for unpatched code, which could be solved on this way. 1) First, new code allows to use complex arithmetic for implementation of mathematical functions without special "corner cases". Take the inverse hyperbolic sine as an example: >>> z = complex(-0.0, 2) >>> cmath.asinh(z) (-1.3169578969248166+1.5707963267948966j) >>> # naive textbook formula doesn't work: >>> cmath.log(z + cmath.sqrt(1 + z*z)) (1.3169578969248166+1.5707963267948966j) >>> # "fixed" version does: >>> cmath.log(z + cmath.sqrt(complex(1 + (z*z).real, (z*z).imag))) (-1.3169578969248164+1.5707963267948966j) 2) Previously, we have only unsigned imaginary literals with the following semantics: ±a±bj = complex(±float(a), 0.0) ± complex(0.0, float(b)) While this behaviour was well documented, most users would expect instead here: ±a±bj = complex(±float(a), ±float(b)) i.e. that it follows to the rectangular notation for complex numbers. Things are worse, because the CPython docs sometimes asserts that the rectangular form is used and that some simple invariants holds. For example, sphinx docs for the complex class says: "complex(real=0, imag=0) ... Return a complex number with the value real + imag*1j ...". But: >>> complex(0.0, cmath.inf) infj >>> 0.0 + cmath.inf*1j (nan+infj) 3) The ``eval(repr(x)) == x`` invariant was broken for the complex type. Below are simple examples with signed zero: >>> complex(-0.0, 1.0) # also note funny signed integer zero below (-0+1j) >>> -0+1j 1j >> -(0.0-1j) # "correct" input for above with Python numeric literals (-0+1j) >>> -(0-1j) # also "correct"; integer 0 matters! (-0+1j) >>> complex(1.0, -0.0) (1-0j) >>> 1-0j (1+0j) >>> -(-1 + 0j) (1-0j) Similar is true for complex numbers with other special components: >>> complex(0.0, -cmath.inf) -infj >>> -cmath.infj (-0-infj)
…hmetic "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. That's why C standards since C99 introduce imaginary types. This patch proposes similar extension to the Python language: * Added a new subtype (imaginary) of the complex type. New type has few overloaded methods (conjugate() and __getnewargs__()). * Complex and imaginary types implement IEC 60559-compatible complex arithmetic (as specified by C11 Annex G). * Imaginary literals now produce instances of imaginary type. * cmath.infj/nanj were changed to be of imaginary type. * Modules ast, code, copy, marshal got support for imaginary type. * Few tests adapted to use complex, instead of imaginary literals - Lib/test/test_fractions.py - Lib/test/test_socket.py - Lib/test/test_str.py Lets consider some (actually interrelated) problems, shown for unpatched code, which could be solved on this way. 1) First, new code allows to use complex arithmetic for implementation of mathematical functions without special "corner cases". Take the inverse hyperbolic sine as an example: >>> z = complex(-0.0, 2) >>> cmath.asinh(z) (-1.3169578969248166+1.5707963267948966j) >>> # naive textbook formula doesn't work: >>> cmath.log(z + cmath.sqrt(1 + z*z)) (1.3169578969248166+1.5707963267948966j) >>> # "fixed" version does: >>> cmath.log(z + cmath.sqrt(complex(1 + (z*z).real, (z*z).imag))) (-1.3169578969248164+1.5707963267948966j) 2) Previously, we have only unsigned imaginary literals with the following semantics: ±a±bj = complex(±float(a), 0.0) ± complex(0.0, float(b)) While this behaviour was well documented, most users would expect instead here: ±a±bj = complex(±float(a), ±float(b)) i.e. that it follows to the rectangular notation for complex numbers. Things are worse, because the CPython docs sometimes asserts that the rectangular form is used and that some simple invariants holds. For example, sphinx docs for the complex class says: "complex(real=0, imag=0) ... Return a complex number with the value real + imag*1j ...". But: >>> complex(0.0, cmath.inf) infj >>> 0.0 + cmath.inf*1j (nan+infj) 3) The ``eval(repr(x)) == x`` invariant was broken for the complex type. Below are simple examples with signed zero: >>> complex(-0.0, 1.0) # also note funny signed integer zero below (-0+1j) >>> -0+1j 1j >> -(0.0-1j) # "correct" input for above with Python numeric literals (-0+1j) >>> -(0-1j) # also "correct"; integer 0 matters! (-0+1j) >>> complex(1.0, -0.0) (1-0j) >>> 1-0j (1+0j) >>> -(-1 + 0j) (1-0j) Similar is true for complex numbers with other special components: >>> complex(0.0, -cmath.inf) -infj >>> -cmath.infj (-0-infj)
…hmetic "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. That's why C standards since C99 introduce imaginary types. This patch proposes similar extension to the Python language: * Added a new subtype (imaginary) of the complex type. New type has few overloaded methods (conjugate() and __getnewargs__()). * Complex and imaginary types implement IEC 60559-compatible complex arithmetic (as specified by C11 Annex G). * Imaginary literals now produce instances of imaginary type. * cmath.infj/nanj were changed to be of imaginary type. * Modules ast, code, copy, marshal got support for imaginary type. * Few tests adapted to use complex, instead of imaginary literals - Lib/test/test_fractions.py - Lib/test/test_socket.py - Lib/test/test_str.py Lets consider some (actually interrelated) problems, shown for unpatched code, which could be solved on this way. 1) First, new code allows to use complex arithmetic for implementation of mathematical functions without special "corner cases". Take the inverse hyperbolic sine as an example: >>> z = complex(-0.0, 2) >>> cmath.asinh(z) (-1.3169578969248166+1.5707963267948966j) >>> # naive textbook formula doesn't work: >>> cmath.log(z + cmath.sqrt(1 + z*z)) (1.3169578969248166+1.5707963267948966j) >>> # "fixed" version does: >>> cmath.log(z + cmath.sqrt(complex(1 + (z*z).real, (z*z).imag))) (-1.3169578969248164+1.5707963267948966j) 2) Previously, we have only unsigned imaginary literals with the following semantics: ±a±bj = complex(±float(a), 0.0) ± complex(0.0, float(b)) While this behaviour was well documented, most users would expect instead here: ±a±bj = complex(±float(a), ±float(b)) i.e. that it follows to the rectangular notation for complex numbers. Things are worse, because the CPython docs sometimes asserts that the rectangular form is used and that some simple invariants holds. For example, sphinx docs for the complex class says: "complex(real=0, imag=0) ... Return a complex number with the value real + imag*1j ...". But: >>> complex(0.0, cmath.inf) infj >>> 0.0 + cmath.inf*1j (nan+infj) 3) The ``eval(repr(x)) == x`` invariant was broken for the complex type. Below are simple examples with signed zero: >>> complex(-0.0, 1.0) # also note funny signed integer zero below (-0+1j) >>> -0+1j 1j >> -(0.0-1j) # "correct" input for above with Python numeric literals (-0+1j) >>> -(0-1j) # also "correct"; integer 0 matters! (-0+1j) >>> complex(1.0, -0.0) (1-0j) >>> 1-0j (1+0j) >>> -(-1 + 0j) (1-0j) Similar is true for complex numbers with other special components: >>> complex(0.0, -cmath.inf) -infj >>> -cmath.infj (-0-infj)
@mdickinson, does this looks as an issue for you or we should accept imprecise docs? I don't see good ways to reword docs, while keeping them short and expressive. Not sure, if this is a good idea to revival the d.p.o thread. On another hand, mentioned in the d.p.o variant with the imaginary type isn't explored well. Here is an implementation of the imaginary type & mixed-mode arithmetic rules: skirpichev#1 Among others, that should address current issue. So far major arguments against were:
I think that above implementation is more or less complete to estimate (a): so far most changes limited to arithmetic methods. (b) - this looks to be a strong one. On another hand, this feature of the C99+ standards is optional - that might be an explanation of partial adoption, while in the Python - complex arithmetic is a part of the language. For numeric C libraries, the GSL, for example, has own routines to do mixed-mode arithmetic... |
Currently the signature of complex is:
Both I think that it should support several more limited signatures:
A string and complex number arguments should only be passed as a positional argument. |
@serhiy-storchaka, yes I think this could solve the current issue like proposed in it's description. (I.e. we should remove all mentions of broken identities.) At price of breaking compatibility. It seems, support for imaginary arguments exists since v2.2 (6d6c1a3). Not sure on what ground it was added, I doubt it's practically used.
... on another hand it might be viewed as a bug. Shall I open a separate issue or it would be better to discuss this first on d.p.o? |
+1. I'd particularly like to see the form |
* Passing a string as the "real" keyword argument is now an error; it should only be passed as a single positional argument. * Passing a complex number as the *real* or *imag* argument is now deprecated; it should only be passed as a single positional argument.
* Passing a string as the "real" keyword argument is now an error; it should only be passed as a single positional argument. * Passing a complex number as the *real* or *imag* argument is now deprecated; it should only be passed as a single positional argument.
I think this is the right issue, and no need to open another issue. My plan:
#119620 implements the last two items. |
* Passing a string as the "real" keyword argument is now an error; it should only be passed as a single positional argument. * Passing a complex number as the *real* or *imag* argument is now deprecated; it should only be passed as a single positional argument.
* Share common classes. * Use exactly representable floats and exact tests. * Check the sign of zero components. * Remove duplicated tests (mostly left after merging int and long). * Reorder tests in more consistent way. * Test more error messages. * Add tests for missed cases.
And #119687 is the documentation part. It adds more details and avoids using the |
* Share common classes. * Use exactly representable floats and exact tests. * Check the sign of zero components. * Remove duplicated tests (mostly left after merging int and long). * Reorder tests in more consistent way. * Test more error messages. * Add tests for missed cases.
…GH-119635) * Share common classes. * Use exactly representable floats and exact tests. * Check the sign of zero components. * Remove duplicated tests (mostly left after merging int and long). * Reorder tests in more consistent way. * Test more error messages. * Add tests for missed cases. (cherry picked from commit bf098d4) Co-authored-by: Serhiy Storchaka <storchaka@gmail.com>
…GH-119635) * Share common classes. * Use exactly representable floats and exact tests. * Check the sign of zero components. * Remove duplicated tests (mostly left after merging int and long). * Reorder tests in more consistent way. * Test more error messages. * Add tests for missed cases. (cherry picked from commit bf098d4) Co-authored-by: Serhiy Storchaka <storchaka@gmail.com>
…9635) (GH-119796) * Share common classes. * Use exactly representable floats and exact tests. * Check the sign of zero components. * Remove duplicated tests (mostly left after merging int and long). * Reorder tests in more consistent way. * Test more error messages. * Add tests for missed cases. (cherry picked from commit bf098d4) Co-authored-by: Serhiy Storchaka <storchaka@gmail.com>
…9687) * Remove the equivalence with real+imag*1j which can be incorrect in corner cases (non-finite numbers, the sign of zeroes). * Separately document the three roles of the constructor: parsing a string, converting a number, and constructing a complex from components. * Document positional-only parameters of complex(), float(), int() and bool() as positional-only. * Add examples for complex() and int(). * Specify the grammar of the string for complex(). * Improve the grammar of the string for float(). * Describe more explicitly the behavior when real and/or imag arguments are complex numbers. (This will be deprecated in future.)
…ythonGH-119687) * Remove the equivalence with real+imag*1j which can be incorrect in corner cases (non-finite numbers, the sign of zeroes). * Separately document the three roles of the constructor: parsing a string, converting a number, and constructing a complex from components. * Document positional-only parameters of complex(), float(), int() and bool() as positional-only. * Add examples for complex() and int(). * Specify the grammar of the string for complex(). * Improve the grammar of the string for float(). * Describe more explicitly the behavior when real and/or imag arguments are complex numbers. (This will be deprecated in future.) (cherry picked from commit ec1ba26) Co-authored-by: Serhiy Storchaka <storchaka@gmail.com>
…ructor (pythonGH-119687) * Remove the equivalence with real+imag*1j which can be incorrect in corner cases (non-finite numbers, the sign of zeroes). * Separately document the three roles of the constructor: parsing a string, converting a number, and constructing a complex from components. * Document positional-only parameters of complex(), float(), int() and bool() as positional-only. * Add examples for complex() and int(). * Specify the grammar of the string for complex(). * Improve the grammar of the string for float(). * Describe more explicitly the behavior when real and/or imag arguments are complex numbers. (This will be deprecated in future.) (cherry picked from commit ec1ba26) Co-authored-by: Serhiy Storchaka <storchaka@gmail.com>
Thank you for your reviews @skirpichev and @mdickinson. |
…GH-119687) (GH-119803) * Remove the equivalence with real+imag*1j which can be incorrect in corner cases (non-finite numbers, the sign of zeroes). * Separately document the three roles of the constructor: parsing a string, converting a number, and constructing a complex from components. * Document positional-only parameters of complex(), float(), int() and bool() as positional-only. * Add examples for complex() and int(). * Specify the grammar of the string for complex(). * Improve the grammar of the string for float(). * Describe more explicitly the behavior when real and/or imag arguments are complex numbers. (This will be deprecated in future.) (cherry picked from commit ec1ba26) Co-authored-by: Serhiy Storchaka <storchaka@gmail.com>
…GH-119687) (ПР-119805) * Remove the equivalence with real+imag*1j which can be incorrect in corner cases (non-finite numbers, the sign of zeroes). * Separately document the three roles of the constructor: parsing a string, converting a number, and constructing a complex from components. * Document positional-only parameters of complex(), float(), int() and bool() as positional-only. * Add examples for complex() and int(). * Specify the grammar of the string for complex(). * Improve the grammar of the string for float(). * Describe more explicitly the behavior when real and/or imag arguments are complex numbers. (This will be deprecated in future.) (cherry picked from commit ec1ba26)
…GH-119635) * Share common classes. * Use exactly representable floats and exact tests. * Check the sign of zero components. * Remove duplicated tests (mostly left after merging int and long). * Reorder tests in more consistent way. * Test more error messages. * Add tests for missed cases.
…ythonGH-119687) * Remove the equivalence with real+imag*1j which can be incorrect in corner cases (non-finite numbers, the sign of zeroes). * Separately document the three roles of the constructor: parsing a string, converting a number, and constructing a complex from components. * Document positional-only parameters of complex(), float(), int() and bool() as positional-only. * Add examples for complex() and int(). * Specify the grammar of the string for complex(). * Improve the grammar of the string for float(). * Describe more explicitly the behavior when real and/or imag arguments are complex numbers. (This will be deprecated in future.)
…ythonGH-119620) * Passing a string as the "real" keyword argument is now an error; it should only be passed as a single positional argument. * Passing a complex number as the "real" or "imag" argument is now deprecated; it should only be passed as a single positional argument.
…GH-119635) * Share common classes. * Use exactly representable floats and exact tests. * Check the sign of zero components. * Remove duplicated tests (mostly left after merging int and long). * Reorder tests in more consistent way. * Test more error messages. * Add tests for missed cases.
…ythonGH-119687) * Remove the equivalence with real+imag*1j which can be incorrect in corner cases (non-finite numbers, the sign of zeroes). * Separately document the three roles of the constructor: parsing a string, converting a number, and constructing a complex from components. * Document positional-only parameters of complex(), float(), int() and bool() as positional-only. * Add examples for complex() and int(). * Specify the grammar of the string for complex(). * Improve the grammar of the string for float(). * Describe more explicitly the behavior when real and/or imag arguments are complex numbers. (This will be deprecated in future.)
…ythonGH-119620) * Passing a string as the "real" keyword argument is now an error; it should only be passed as a single positional argument. * Passing a complex number as the "real" or "imag" argument is now deprecated; it should only be passed as a single positional argument.
…9635) (GH-119795) * Share common classes. * Use exactly representable floats and exact tests. * Check the sign of zero components. * Remove duplicated tests (mostly left after merging int and long). * Reorder tests in more consistent way. * Test more error messages. * Add tests for missed cases. (cherry picked from commit bf098d4) Co-authored-by: Serhiy Storchaka <storchaka@gmail.com>
The sphinx docs says:
The docstring (btw it doesn't mention a string as an argument):
That wrong, e.g.:
Here is an attempt (patch) to solve, let me know if this is worth a PR:
Edit:
Another instance of this issue is in the cmath docs:
E.g.:
Linked PRs
The text was updated successfully, but these errors were encountered: