Skip to content

Commit 8e7dcca

Browse files
author
Release Manager
committed
Trac #33733: allow to use flint for Stirling numbers
of both kind URL: https://trac.sagemath.org/33733 Reported by: chapoton Ticket author(s): Frédéric Chapoton Reviewer(s): Marc Mezzarobba, Travis Scrimshaw
2 parents 6f4efb0 + bbd7807 commit 8e7dcca

File tree

3 files changed

+129
-57
lines changed

3 files changed

+129
-57
lines changed

src/sage/combinat/combinat.py

+75-55
Original file line numberDiff line numberDiff line change
@@ -364,9 +364,9 @@ def bell_number(n, algorithm='flint', **options) -> Integer:
364364
365365
TESTS::
366366
367-
sage: all(bell_number(n) == bell_number(n,'dobinski') for n in range(200))
367+
sage: all(bell_number(n) == bell_number(n,'dobinski') for n in range(100))
368368
True
369-
sage: all(bell_number(n) == bell_number(n,'gap') for n in range(200))
369+
sage: all(bell_number(n) == bell_number(n,'gap') for n in range(100))
370370
True
371371
sage: all(bell_number(n) == bell_number(n,'mpmath', prec=500) for n in range(200, 220))
372372
True
@@ -854,16 +854,23 @@ def lucas_number2(n, P, Q):
854854
return libgap.Lucas(P, Q, n)[1].sage()
855855

856856

857-
def stirling_number1(n, k) -> Integer:
857+
def stirling_number1(n, k, algorithm="gap") -> Integer:
858858
r"""
859859
Return the `n`-th Stirling number `S_1(n,k)` of the first kind.
860860
861861
This is the number of permutations of `n` points with `k` cycles.
862862
863-
This wraps GAP's ``Stirling1``.
864-
865863
See :wikipedia:`Stirling_numbers_of_the_first_kind`.
866864
865+
INPUT:
866+
867+
- ``n`` -- nonnegative machine-size integer
868+
- ``k`` -- nonnegative machine-size integer
869+
- ``algorithm``:
870+
871+
* ``"gap"`` (default) -- use GAP's ``Stirling1`` function
872+
* ``"flint"`` -- use flint's ``arith_stirling_number_1u`` function
873+
867874
EXAMPLES::
868875
869876
sage: stirling_number1(3,2)
@@ -876,31 +883,48 @@ def stirling_number1(n, k) -> Integer:
876883
269325
877884
878885
Indeed, `S_1(n,k) = S_1(n-1,k-1) + (n-1)S_1(n-1,k)`.
886+
887+
TESTS::
888+
889+
sage: stirling_number1(10,5, algorithm='flint')
890+
269325
891+
892+
sage: s_sage = stirling_number1(50,3, algorithm="mutta")
893+
Traceback (most recent call last):
894+
...
895+
ValueError: unknown algorithm: mutta
879896
"""
880897
n = ZZ(n)
881898
k = ZZ(k)
882-
from sage.libs.gap.libgap import libgap
883-
return libgap.Stirling1(n, k).sage()
899+
if algorithm == 'gap':
900+
from sage.libs.gap.libgap import libgap
901+
return libgap.Stirling1(n, k).sage()
902+
if algorithm == 'flint':
903+
import sage.libs.flint.arith
904+
return sage.libs.flint.arith.stirling_number_1(n, k)
905+
raise ValueError("unknown algorithm: %s" % algorithm)
884906

885907

886908
def stirling_number2(n, k, algorithm=None) -> Integer:
887909
r"""
888-
Return the `n`-th Stirling number `S_2(n,k)` of the second
889-
kind.
910+
Return the `n`-th Stirling number `S_2(n,k)` of the second kind.
890911
891912
This is the number of ways to partition a set of `n` elements into `k`
892913
pairwise disjoint nonempty subsets. The `n`-th Bell number is the
893914
sum of the `S_2(n,k)`'s, `k=0,...,n`.
894915
916+
See :wikipedia:`Stirling_numbers_of_the_second_kind`.
917+
895918
INPUT:
896919
897-
* ``n`` - nonnegative machine-size integer
898-
* ``k`` - nonnegative machine-size integer
899-
* ``algorithm``:
920+
- ``n`` -- nonnegative machine-size integer
921+
- ``k`` -- nonnegative machine-size integer
922+
- ``algorithm``:
900923
901-
* None (default) - use native implementation
902-
* ``"maxima"`` - use Maxima's stirling2 function
903-
* ``"gap"`` - use GAP's Stirling2 function
924+
* ``None`` (default) -- use native implementation
925+
* ``"flint"`` -- use flint's ``arith_stirling_number_2`` function
926+
* ``"gap"`` -- use GAP's ``Stirling2`` function
927+
* ``"maxima"`` -- use Maxima's ``stirling2`` function
904928
905929
EXAMPLES:
906930
@@ -922,10 +946,10 @@ def stirling_number2(n, k, algorithm=None) -> Integer:
922946
923947
Stirling numbers satisfy `S_2(n,k) = S_2(n-1,k-1) + kS_2(n-1,k)`::
924948
925-
sage: 5*stirling_number2(9,5) + stirling_number2(9,4)
926-
42525
927-
sage: stirling_number2(10,5)
928-
42525
949+
sage: 5*stirling_number2(9,5) + stirling_number2(9,4)
950+
42525
951+
sage: stirling_number2(10,5)
952+
42525
929953
930954
TESTS::
931955
@@ -961,58 +985,57 @@ def stirling_number2(n, k, algorithm=None) -> Integer:
961985
1900842429486
962986
sage: type(n)
963987
<class 'sage.rings.integer.Integer'>
964-
sage: n = stirling_number2(20,11,algorithm='maxima')
988+
sage: n = stirling_number2(20,11,algorithm='flint')
965989
sage: n
966990
1900842429486
967991
sage: type(n)
968992
<class 'sage.rings.integer.Integer'>
969993
970-
Sage's implementation splitting the computation of the Stirling
971-
numbers of the second kind in two cases according to `n`, let us
972-
check the result it gives agree with both maxima and gap.
973-
974-
For `n<200`::
994+
Sage's implementation splitting the computation of the Stirling
995+
numbers of the second kind in two cases according to `n`, let us
996+
check the result it gives agree with both flint and gap.
975997
976-
sage: for n in Subsets(range(100,200), 5).random_element():
977-
....: for k in Subsets(range(n), 5).random_element():
978-
....: s_sage = stirling_number2(n,k)
979-
....: s_maxima = stirling_number2(n,k, algorithm = "maxima")
980-
....: s_gap = stirling_number2(n,k, algorithm = "gap")
981-
....: if not (s_sage == s_maxima and s_sage == s_gap):
982-
....: print("Error with n<200")
998+
For `n<200`::
983999
984-
For `n\geq 200`::
1000+
sage: for n in Subsets(range(100,200), 5).random_element():
1001+
....: for k in Subsets(range(n), 5).random_element():
1002+
....: s_sage = stirling_number2(n,k)
1003+
....: s_flint = stirling_number2(n,k, algorithm = "flint")
1004+
....: s_gap = stirling_number2(n,k, algorithm = "gap")
1005+
....: if not (s_sage == s_flint and s_sage == s_gap):
1006+
....: print("Error with n<200")
9851007
986-
sage: for n in Subsets(range(200,300), 5).random_element():
987-
....: for k in Subsets(range(n), 5).random_element():
988-
....: s_sage = stirling_number2(n,k)
989-
....: s_maxima = stirling_number2(n,k, algorithm = "maxima")
990-
....: s_gap = stirling_number2(n,k, algorithm = "gap")
991-
....: if not (s_sage == s_maxima and s_sage == s_gap):
992-
....: print("Error with n<200")
1008+
For `n\geq 200`::
9931009
1010+
sage: for n in Subsets(range(200,300), 5).random_element():
1011+
....: for k in Subsets(range(n), 5).random_element():
1012+
....: s_sage = stirling_number2(n,k)
1013+
....: s_flint = stirling_number2(n,k, algorithm = "flint")
1014+
....: s_gap = stirling_number2(n,k, algorithm = "gap")
1015+
....: if not (s_sage == s_flint and s_sage == s_gap):
1016+
....: print("Error with n<200")
9941017
995-
TESTS:
1018+
sage: stirling_number2(20,3, algorithm="maxima")
1019+
580606446
9961020
997-
Checking an exception is raised whenever a wrong value is given
998-
for ``algorithm``::
999-
1000-
sage: s_sage = stirling_number2(50,3, algorithm = "CloudReading")
1001-
Traceback (most recent call last):
1002-
...
1003-
ValueError: unknown algorithm: CloudReading
1021+
sage: s_sage = stirling_number2(5,3, algorithm="namba")
1022+
Traceback (most recent call last):
1023+
...
1024+
ValueError: unknown algorithm: namba
10041025
"""
10051026
n = ZZ(n)
10061027
k = ZZ(k)
10071028
if algorithm is None:
10081029
return _stirling_number2(n, k)
1009-
elif algorithm == 'gap':
1030+
if algorithm == 'gap':
10101031
from sage.libs.gap.libgap import libgap
10111032
return libgap.Stirling2(n, k).sage()
1012-
elif algorithm == 'maxima':
1033+
if algorithm == 'flint':
1034+
import sage.libs.flint.arith
1035+
return sage.libs.flint.arith.stirling_number_2(n, k)
1036+
if algorithm == 'maxima':
10131037
return ZZ(maxima.stirling2(n, k)) # type:ignore
1014-
else:
1015-
raise ValueError("unknown algorithm: %s" % algorithm)
1038+
raise ValueError("unknown algorithm: %s" % algorithm)
10161039

10171040

10181041
def polygonal_number(s, n):
@@ -1404,8 +1427,6 @@ def __bool__(self) -> bool:
14041427
"""
14051428
return bool(self._list)
14061429

1407-
1408-
14091430
def __len__(self) -> Integer:
14101431
"""
14111432
EXAMPLES::
@@ -2442,7 +2463,6 @@ def __iter__(self):
24422463

24432464
##############################################################################
24442465
from sage.sets.image_set import ImageSubobject
2445-
from sage.categories.map import is_Map
24462466

24472467

24482468
class MapCombinatorialClass(ImageSubobject, CombinatorialClass):

src/sage/libs/flint/arith.pxd

+4-2
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,15 @@
11
# distutils: libraries = flint
22
# distutils: depends = flint/arith.h
33

4-
from sage.libs.flint.types cimport fmpz_t, fmpq_t, ulong
4+
from sage.libs.flint.types cimport fmpz_t, fmpq_t, ulong, slong
55

66
# flint/arith.h
77
cdef extern from "flint_wrap.h":
88
void arith_bell_number(fmpz_t b, ulong n)
99
void arith_bernoulli_number(fmpq_t x, ulong n)
10-
void arith_euler_number ( fmpz_t res , ulong n )
10+
void arith_euler_number(fmpz_t res, ulong n)
11+
void arith_stirling_number_1u(fmpz_t res, slong n, slong k)
12+
void arith_stirling_number_2(fmpz_t res, slong n, slong k)
1113
void arith_number_of_partitions(fmpz_t x, ulong n)
1214
void arith_dedekind_sum(fmpq_t, fmpz_t, fmpz_t)
1315
void arith_harmonic_number(fmpq_t, unsigned long n)

src/sage/libs/flint/arith.pyx

+50
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,56 @@ def euler_number(unsigned long n):
116116
return ans
117117

118118

119+
def stirling_number_1(long n, long k):
120+
"""
121+
Return the unsigned Stirling number of the first kind.
122+
123+
EXAMPLES::
124+
125+
sage: from sage.libs.flint.arith import stirling_number_1
126+
sage: [stirling_number_1(8,i) for i in range(9)]
127+
[0, 5040, 13068, 13132, 6769, 1960, 322, 28, 1]
128+
"""
129+
cdef fmpz_t ans_fmpz
130+
cdef Integer ans = Integer(0)
131+
132+
fmpz_init(ans_fmpz)
133+
134+
if n > 1000:
135+
sig_on()
136+
arith_stirling_number_1u(ans_fmpz, n, k)
137+
fmpz_get_mpz(ans.value, ans_fmpz)
138+
fmpz_clear(ans_fmpz)
139+
if n > 1000:
140+
sig_off()
141+
return ans
142+
143+
144+
def stirling_number_2(long n, long k):
145+
"""
146+
Return the Stirling number of the second kind.
147+
148+
EXAMPLES::
149+
150+
sage: from sage.libs.flint.arith import stirling_number_2
151+
sage: [stirling_number_2(8,i) for i in range(9)]
152+
[0, 1, 127, 966, 1701, 1050, 266, 28, 1]
153+
"""
154+
cdef fmpz_t ans_fmpz
155+
cdef Integer ans = Integer(0)
156+
157+
fmpz_init(ans_fmpz)
158+
159+
if n > 1000:
160+
sig_on()
161+
arith_stirling_number_2(ans_fmpz, n, k)
162+
fmpz_get_mpz(ans.value, ans_fmpz)
163+
fmpz_clear(ans_fmpz)
164+
if n > 1000:
165+
sig_off()
166+
return ans
167+
168+
119169
def number_of_partitions(unsigned long n):
120170
"""
121171
Return the number of partitions of the integer `n`.

0 commit comments

Comments
 (0)