Open Issues:
- module name (imath, intmath, ntheory, integermath, imaths, ???); poll is here
- possible extensions (some guideline to accept/reject potential module content)
Previous discussions:
Open Issues:
Previous discussions:
From the PEP text, the only motivation I see listed is the complexity of the existing module (which I guess implicitly explains why the new functions aren’t simply added to math
). Is this accurate? Or is there some other reason why this would be useful as its own module?
My suspicion right now is that this proposal is ranking purity over pragmatism, but I’m willing to believe there are pragmatic reasons to move functionality to a different place - those reasons need to be in the PEP text though
Could you add a submodule (e.g. math.integer
) as an option? Either selected or rejected I don’t mind, but it is the usual way that we reduce implementation complexity,[1] so it’s a notable omission from the proposal.
When that complexity is described using LOC. ↩︎
- module name (imath, intmath, ntheory, integermath, imaths, ???)
Given the existence of cmath
, I can see how naming it imath
makes sense.
Historically, the i
prefix has (mostly?) been used for “iterative” things, for example itertools.islice
, and itertools.izip
on Python 2. But given the (*math
) context, I don’t expect that this is likely to become a common source of confusion, especially for those that already know about cmath
.
Falling and rising factorials (wiki) could be handy to have. In the (underrated) statistical field of L-moments, they tend to pop up all over the place (see e.g. Distributions - Lmo). And in discrete calculus the falling factorial plays a crucial role in discrete derivatives.
The multinomial coefficient might also make for a nice extension, as well as the Lah numbers, Catalan numbers, and the Stirling numbers of the first and second kind.
I would just like to suggest the name zmath
. This would be consistent with set theory notation of \mathbb{C} for complex (cmath
), and \mathbb{Z} for integers (zmath
). Google, Bing, Duckduckgo all point to integers with the search query z mathematics
or z math
. The same can’t be said for i math
, which promptly surfaces imaginary numbers. There’s a pypi package named ZMATH, which is not reachable.
I don’t feel strongly about this. It is just a suggestion as the question of naming is open.
Sorry, but this looks like it will just make things backwards incompatible with very little benefit.
The math
module seems like a fine home for these, since they are for doing (certain types of) math, and new Python users probably have no idea of what C’s libmath even is. In comparison, the cmath
module is separate for a very different reason – it provides some of the same functions but implemented for complex numbers (we chose to do it this way rather than just returning a complex from e.g. math.sqrt(-1)
since users of math
were used to getting an error for that).
Separating things now, at this late stage, just causes pain for users who are upgrading without benefits. And keeping the aliases in math
forever is even less ideal.
Previous discussion: Split out integer functions from the math module
That’s acceptable to me. But, in return, I never again want to hear the decades-old argument “but we can’t add that to math
- that’s for functions on floats!”
There are large areas of “just math” that all look the same to newbies, but are very different to experienced practitioners. That’s presumably why we have a statistics
module. Number theory in turn has a large number of exact integer-argument functions that have nothing material in common with the float-domain functions.
All currently-working code would continue to work as before. No changes needed.
There is no pure win to be had here. The intent is to stop the conceptual incoherence of the current math
module from getting ever worse over time. Of course “coherence” depends on one’s experience. Failing to draw distinctions that are glaringly obvious to the more experienced is a form of incoherence to them.
The number-theory functions don’t belong in math
any more than, e.g., it would have made conceptual sense to have added limit_denominator()
to math
, as a function that only accepts a Fraction
argument, instead of (as was done) making it a method of Fraction
. That’s all “just math” too. The functions in question here only accept integer arguments, and in a purely OO language would be methods of int
.
Luckily, Python will survive either way
Just like others I don’t understand the impulse of doing this.
The PEP claims:
over time the module was populated with functions that aren’t related to the C standard or floating-point arithmetics. Now it’s much harder to describe module scope, content and interfaces (returned values or accepted arguments).
Well, it’s not really harder. Just change the docs to point that the module provides mathematical functions (which is obvious from its name, anyway, and much more understandable to the average Python developer than some mythical beast named “the C standard”).
1j**2
on this
They’re already implemented with some care for bigint speed, although that may not be obvious at first glance:
>>> import math
>>> math.perm(7, 2)
42
So that’s the falling factorial of 7 for 2 terms, or, equivalently, the rising factorial of 6 for 2 terms.
>>> math.log10(math.perm(1000000, 1000))
5999.782997596272
That goes fast! Python will never be speed-competitive with GMP for such things, though.
Random thought: If these functions are all associated to the int
type, why not add them as int
methods? For example:
>>> (15).isqrt()
3
>>> (4).factorial()
24
>>> (5).choose(3) # I believe humans tend to pronounce `comb` this way
10
And for what it’s worth, it’s pretty similar to how Rust deals with integer maths:
let n: i32 = 15;
assert_eq!(n.isqrt(), 3);
Any and all additions to this collection should probably go into third-party packages hosted on PyPI anyway.
That was done for int.bit_length()
and int.bit_count()
. There’s no consistent “logic” to this. At heart, Python is not, and does not aspire to be, a purely OO language. There are centuries of literature and prior art that “spell” the high-use integer functions (like gcd()
, factorial()
, etc) in some form of functional notation. object.method()
notation is a new-fangled thing and not necessarily an improvement for usability .
When this module was first proposed 7 years ago, there were only two integer-related functions in math
: factorial()
and gcd()
. It would be much easier to add it at that time. Since then, 4 new functions were added, factorial()
stopped supporting floats, gcd()
was extended. Now, implementation of these 6 functions takes 1/3 of the math
module volume.
Several functions that I planned to add to the new module have not yet been added to math
, partially because it is difficult to support them intermixed with floating point functions in one file:
ilog()
– determines the number of digits of a number in a given number system.as_integer_ratio()
– represents a number as an integer ratio.datetime
and fractions
.isprime()
predicate and primes()
generator.isqrt()
and ilog()
with rounding up.The more we delay adding a new module, the higher the price.
That’s an odd meaning for “batteries included” . The integer-domain functions find increasing use in a world where crypto- and AI-related technologies attract ever-increasing billions of dollars worth of attention.
CPython versions will never be speed-competitive with the fastest out there, but I’m very sympathetic too to the “merely curious” who want to putz around with their own code experiments. Some non-trivial level of “batteries included” is a large part of what made Python fun to use. Not just the language, but the broad coverage of the out-of-the-box modules. CPython’s developers put a lot of care into designing & building those too, and the quality shows.
Python is also “a brand” now too, and inclusion in the core distribution is an assurance of quality few 3rd-party packages can hope to offer.
Need Stirling numbers of the second kind? SciPy
offers it, but that’s such a large system last time I needed them I wrote one myself.
Not saying Python needs stirling2(n, k)
, but am saying I don’t reject the idea without a hearing. If a case can be made for it, it certainly doesn’t belong in math
, though .
At the very least, pull the int functions out of mathmodule.c
and into their own file. It can simply be #include
d.
Since very few people work on this code, they can’t be expected to grasp the pragmatics. In part, in some respects it takes a very different mindset to work on exact integer functions than approximate float ones, and it’s an unhelpful distraction for that reason alone to have such very differerent kinds of code in the same file.
No. It’s only minor point, the last one in the Motivation section. You could say it’s something implementation-dependent: i.e. Java’s BigInteger class has sqrt() and gcd() for free, GraalPy probably will just call that stuff.
Yes! The main motivation is that these functions looks very different wrt the rest of the module. Other return values, other convention for processing arguments.
Last but not least — a different application domain. I rarely need libm’s-borrowed functions when I import number-theoretic stuff from the math module and vice versa. (So, doing from math import *
just pollute my namespace with unwanted stuff either way. Same for math.<tab>
in IDE/CLI. Though, this seems as issues mostly while interactive work.)
Thanks, I’ll do. In the end, this will look as a different option for naming. And that remove possible conflicts with external projects.
Obviously, any function, that satisfy two constraints (accept integers, compute results exactly) — is a possible candidate.
I would expect a different border line: if we need really advanced algorithms to do things with reasonable time/space constraints — that’s rather belongs to some specialized package on the PyPI.
As a small addition to Tim’s comment, I could say that functions in the proposed module will work also for integer-like objects (those, that have the __index__
special method).
Concrete suggestions are welcome.
Which mathematical functions? The cmath or statistics modules have them a lot!
As a bit of off-topic, did you consider to have some global flag(s) to control this? E.g. gmpy2 or mpmath have a context notion. In gmpy2, where the CPython’s counterpart have ValueError for domain error — you can get either nan/inf (per C standard), or an exception, or a complex result:
>>> import gmpy2
>>> gmpy2.sqrt(-1)
mpfr('nan')
>>> gmpy2.get_context().trap_invalid=True
>>> gmpy2.sqrt(-1)
Traceback (most recent call last):
File "<python-input-4>", line 1, in <module>
gmpy2.sqrt(-1)
~~~~~~~~~~^^^^
gmpy2.InvalidOperationError: invalid operation
>>> gmpy2.get_context().allow_complex = True
>>> gmpy2.sqrt(-1)
mpc('0.0+1.0j')
I can’t add much more to Tim’s and Serhyi’s comments. My 2c:
IIUIC, the “soft deprecation” doesn’t mean we will have to keep them forever. Eventually, we can deprecate these aliases. So, I doubt that backward compatibility or maintenance burden to keep aliases are good arguments against.
How about suggested by Steve submodule, say math.ntheory
? The math
module holds “catch-all” role for math, but integer-specific functions will have own namespace?