-
-
Notifications
You must be signed in to change notification settings - Fork 629
Add Lah numbers and clean up combinatorics section #39379
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
base: develop
Are you sure you want to change the base?
Conversation
a6c1a03
to
8a5f23d
Compare
Documentation preview for this PR (built with commit 217b186; changes) is ready! 🎉 |
8a5f23d
to
dd56131
Compare
dd56131
to
1e9ee3e
Compare
While we're at it, maybe |
Not really, at least on my machine: sage: def lah_numbers4(n, k):
....: if k > n:
....: return 0
....: if k == 0:
....: return 0 if n else 1
....: n = Integer(n)
....: k = Integer(k)
....: a = n.factorial() // k.factorial()
....: return a**2*k // (n*(n-k).factorial())
....:
....: def lah_numbers5(n, k):
....: if k > n:
....: return 0
....: if k == 0:
....: return 0 if n else 1
....: n = Integer(n)
....: k = Integer(k)
....: a = binomial(n, k)*(n-k).factorial()
....: return a**2*k // (n*(n-k).factorial())
....:
sage: timeit('T1 = matrix([[lah_numbers4(n,k) for n in range(100)] for k in rang
....: e(100)])')
25 loops, best of 3: 9.74 ms per loop
sage: timeit('T2 = matrix([[lah_numbers5(n,k) for n in range(100)] for k in rang
....: e(100)])')
5 loops, best of 3: 116 ms per loop
sage: T1 = matrix([[lah_numbers4(n,k) for k in range(10)] for n in range(10)])
sage: T2 = matrix([[lah_numbers5(n,k) for k in range(10)] for n in range(10)])
sage: T1 == T2
True |
Apparently there's massive overhead in Try this one
(note that In my experiment, it has minimal overhead for small n and k (as in your matrix) and a few times faster for e.g. It looks impossible to have a version that calls |
Voilà! sage: def lah_numbers4(n, k):
....: if k > n:
....: return 0
....: if k == 0:
....: return 0 if n else 1
....: n = Integer(n)
....: k = Integer(k)
....: a = n.factorial() // k.factorial()
....: return a**2*k // (n*(n-k).factorial())
....:
sage: def lah_numbers6(n, k):
....: if k > n:
....: return 0
....: if k == 0:
....: return 0 if n else 1
....: n = Integer(n)
....: k = Integer(k)
....: a = n.binomial(k)
....: return a * k // n * a * (n-k).factorial()
....:
sage: timeit('T1 = matrix([[lah_numbers4(n,k) for n in range(100)] for k in range(100)])')
25 loops, best of 3: 9.36 ms per loop
sage: timeit('T1 = matrix([[lah_numbers6(n,k) for n in range(100)] for k in range(100)])')
25 loops, best of 3: 11.1 ms per loop
sage: timeit('T1 = matrix([[lah_numbers4(10000+n,10000+k) for n in range(100)] for k in range(100)])')
5 loops, best of 3: 1.91 s per loop
sage: timeit('T2 = matrix([[lah_numbers6(10000+n,10000+k) for n in range(100)] for k in range(100)])')
25 loops, best of 3: 20.7 ms per loop
sage: timeit('T1 = matrix([[lah_numbers4(10000+n,k) for n in range(100)] for k in range(100)])')
5 loops, best of 3: 10.7 s per loop
sage: timeit('T2 = matrix([[lah_numbers6(10000+n,k) for n in range(100)] for k in range(100)])')
5 loops, best of 3: 2.19 s per loop Nice! |
260df5f
to
42ef17b
Compare
42ef17b
to
02e13e9
Compare
b5d05e2
to
44c3dc1
Compare
44c3dc1
to
7a10747
Compare
f7695e8
to
e785a89
Compare
sagemathgh-39891: Speed up binomial As first observed in sagemath#39379, when `n` and `k` are small sage integers, `binomial(n, k)` are much slower than `n.binomial(k)`. This pull request fixes it. Now `%timeit binomial(20, 10)` is only twice slower than `%timeit 20.binomial(10)` — previously it was **30** times slower. There's also a minor patch where some code is skipped when all arguments are instances of `Element`. It is certainly a more common case for the input to be `Element` than otherwise, so we optimize for it. Another minor patch involves storing imported modules as C global variables instead of re-import every time. I think using not-overriding `_method_arguments` as an indicator of "don't call the method" (then silently catch `TypeError` and throw it away) is rather bad design, but this is not in scope here. ### 📝 Checklist <!-- Put an `x` in all the boxes that apply. --> - [x] The title is concise and informative. - [x] The description explains in detail what this PR is about. - [x] I have linked a relevant issue or discussion. - [x] I have created tests covering the changes. - [ ] I have updated the documentation and checked the documentation preview. URL: sagemath#39891 Reported by: user202729 Reviewer(s): Travis Scrimshaw
sagemathgh-39891: Speed up binomial As first observed in sagemath#39379, when `n` and `k` are small sage integers, `binomial(n, k)` are much slower than `n.binomial(k)`. This pull request fixes it. Now `%timeit binomial(20, 10)` is only twice slower than `%timeit 20.binomial(10)` — previously it was **30** times slower. There's also a minor patch where some code is skipped when all arguments are instances of `Element`. It is certainly a more common case for the input to be `Element` than otherwise, so we optimize for it. Another minor patch involves storing imported modules as C global variables instead of re-import every time. I think using not-overriding `_method_arguments` as an indicator of "don't call the method" (then silently catch `TypeError` and throw it away) is rather bad design, but this is not in scope here. ### 📝 Checklist <!-- Put an `x` in all the boxes that apply. --> - [x] The title is concise and informative. - [x] The description explains in detail what this PR is about. - [x] I have linked a relevant issue or discussion. - [x] I have created tests covering the changes. - [ ] I have updated the documentation and checked the documentation preview. URL: sagemath#39891 Reported by: user202729 Reviewer(s): Travis Scrimshaw
sagemathgh-39891: Speed up binomial As first observed in sagemath#39379, when `n` and `k` are small sage integers, `binomial(n, k)` are much slower than `n.binomial(k)`. This pull request fixes it. Now `%timeit binomial(20, 10)` is only twice slower than `%timeit 20.binomial(10)` — previously it was **30** times slower. There's also a minor patch where some code is skipped when all arguments are instances of `Element`. It is certainly a more common case for the input to be `Element` than otherwise, so we optimize for it. Another minor patch involves storing imported modules as C global variables instead of re-import every time. I think using not-overriding `_method_arguments` as an indicator of "don't call the method" (then silently catch `TypeError` and throw it away) is rather bad design, but this is not in scope here. ### 📝 Checklist <!-- Put an `x` in all the boxes that apply. --> - [x] The title is concise and informative. - [x] The description explains in detail what this PR is about. - [x] I have linked a relevant issue or discussion. - [x] I have created tests covering the changes. - [ ] I have updated the documentation and checked the documentation preview. URL: sagemath#39891 Reported by: user202729 Reviewer(s): Travis Scrimshaw
3b0e04a
to
5c38935
Compare
5c38935
to
1ee62d8
Compare
@fchapoton As you currently seem to be in the "combinat" section, would you review this PR? This PR touches many files in the directory The new code for Lah numbers was, I think, already reviewed by @user202729. |
According to the Wikipedia article https://en.wikipedia.org/wiki/Lah_number, a formula that computes the Lah number$L(n,k)$ , also called the Stirling numbers of the third kind, is
but the most efficient form of the formula seems to be
contributed by @user202729.
Long time ago, #17161 proposed yet another formula that gives$L(n,k)$ for all integers $n,k$ . But that formula is extremely slow when implemented in Sage. Like existing functions for Stirling numbers, we only support the main case where $n$ and $k$ are nonnegative.
After this PR, we may close #17161, #17159, #17123 since we do not intend to support the negative$k$ case for combinatorial functions, which is I think only of minor interest and in controversy.
I made some cosmetic changes to the file
src/sage/combinat/combinat.py
, mostly for user friendliness.While at it, I ended up making extensive changes to index pages of the combinatorics section, for tidiness. In particular,
and also fixed #17421.
📝 Checklist
⌛ Dependencies