From 4a4b9c9919574321ccd090a2d1b0a45d4e7b6a1b Mon Sep 17 00:00:00 2001 From: Lorenz Panny Date: Tue, 3 Oct 2023 12:53:14 +0200 Subject: [PATCH 1/6] =?UTF-8?q?add=20simple=20function=20to=20count=20irre?= =?UTF-8?q?ducible=20polynomials=20over=20=F0=9D=94=BDq?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/sage/arith/all.py | 1 + src/sage/arith/misc.py | 37 +++++++++++++++++++++++++++++++++++++ 2 files changed, 38 insertions(+) diff --git a/src/sage/arith/all.py b/src/sage/arith/all.py index 3446336de68..5dff477a3b1 100644 --- a/src/sage/arith/all.py +++ b/src/sage/arith/all.py @@ -81,6 +81,7 @@ prime_factors, prime_range, valuation, + number_of_irreducible_polynomials, ) lazy_import("sage.arith.misc", ("Sigma", "Moebius", "Euler_Phi"), deprecation=30322) diff --git a/src/sage/arith/misc.py b/src/sage/arith/misc.py index eef9d79e121..54f55c79165 100644 --- a/src/sage/arith/misc.py +++ b/src/sage/arith/misc.py @@ -6383,3 +6383,40 @@ def dedekind_psi(N): """ N = Integer(N) return Integer(N * prod(1 + 1 / p for p in N.prime_divisors())) + + +def number_of_irreducible_polynomials(q, n): + r""" + Return the number of irreducible polynomials of degree ``n`` + over the finite field with ``q`` elements. + + INPUT: + + - ``q`` -- prime power + - ``n`` -- positive integer + + OUTPUT: integer + + EXAMPLES:: + + sage: number_of_irreducible_polynomials(2, 8) + 30 + sage: number_of_irreducible_polynomials(9, 9) + 43046640 + + This function is *much* faster than enumerating the polynomials:: + + sage: num = number_of_irreducible_polynomials(101, 99) + sage: num.bit_length() + 653 + + ALGORITHM: + + Classical formula `\frac1n \sum_{d\mid n} \mu(n/d) q^d` using the + Möbius function `\mu`; see :func:`moebius`. + """ + q, n = ZZ(q), ZZ(n) + r = ZZ.zero() + for d in n.divisors(): + r += moebius(n//d) * q**d + return r // n From f65167cec1641fca487a69e1fa65928fa14d724c Mon Sep 17 00:00:00 2001 From: Lorenz Panny Date: Thu, 5 Oct 2023 13:21:40 +0200 Subject: [PATCH 2/6] move from sage.arith.misc to sage.combinat.q_analogues --- src/sage/arith/all.py | 1 - src/sage/arith/misc.py | 37 ------------------------------- src/sage/combinat/all.py | 2 +- src/sage/combinat/q_analogues.py | 38 ++++++++++++++++++++++++++++++++ 4 files changed, 39 insertions(+), 39 deletions(-) diff --git a/src/sage/arith/all.py b/src/sage/arith/all.py index 5dff477a3b1..3446336de68 100644 --- a/src/sage/arith/all.py +++ b/src/sage/arith/all.py @@ -81,7 +81,6 @@ prime_factors, prime_range, valuation, - number_of_irreducible_polynomials, ) lazy_import("sage.arith.misc", ("Sigma", "Moebius", "Euler_Phi"), deprecation=30322) diff --git a/src/sage/arith/misc.py b/src/sage/arith/misc.py index 54f55c79165..eef9d79e121 100644 --- a/src/sage/arith/misc.py +++ b/src/sage/arith/misc.py @@ -6383,40 +6383,3 @@ def dedekind_psi(N): """ N = Integer(N) return Integer(N * prod(1 + 1 / p for p in N.prime_divisors())) - - -def number_of_irreducible_polynomials(q, n): - r""" - Return the number of irreducible polynomials of degree ``n`` - over the finite field with ``q`` elements. - - INPUT: - - - ``q`` -- prime power - - ``n`` -- positive integer - - OUTPUT: integer - - EXAMPLES:: - - sage: number_of_irreducible_polynomials(2, 8) - 30 - sage: number_of_irreducible_polynomials(9, 9) - 43046640 - - This function is *much* faster than enumerating the polynomials:: - - sage: num = number_of_irreducible_polynomials(101, 99) - sage: num.bit_length() - 653 - - ALGORITHM: - - Classical formula `\frac1n \sum_{d\mid n} \mu(n/d) q^d` using the - Möbius function `\mu`; see :func:`moebius`. - """ - q, n = ZZ(q), ZZ(n) - r = ZZ.zero() - for d in n.divisors(): - r += moebius(n//d) * q**d - return r // n diff --git a/src/sage/combinat/all.py b/src/sage/combinat/all.py index 79690ff7305..d1f391013e2 100644 --- a/src/sage/combinat/all.py +++ b/src/sage/combinat/all.py @@ -247,7 +247,7 @@ from .integer_vector_weighted import WeightedIntegerVectors from .integer_vectors_mod_permgroup import IntegerVectorsModPermutationGroup -lazy_import('sage.combinat.q_analogues', ['gaussian_binomial', 'q_binomial']) +lazy_import('sage.combinat.q_analogues', ['gaussian_binomial', 'q_binomial', 'number_of_irreducible_polynomials']) from .species.all import * diff --git a/src/sage/combinat/q_analogues.py b/src/sage/combinat/q_analogues.py index f31cbe41589..3d84552c47a 100644 --- a/src/sage/combinat/q_analogues.py +++ b/src/sage/combinat/q_analogues.py @@ -974,3 +974,41 @@ def q_stirling_number2(n, k, q=None): return parent(q)(0) return (q**(k-1)*q_stirling_number2(n - 1, k - 1, q=q) + q_int(k, q=q) * q_stirling_number2(n - 1, k, q=q)) + + +def number_of_irreducible_polynomials(q, n): + r""" + Return the number of irreducible polynomials of degree ``n`` + over the finite field with ``q`` elements. + + INPUT: + + - ``q`` -- prime power + - ``n`` -- positive integer + + OUTPUT: integer + + EXAMPLES:: + + sage: number_of_irreducible_polynomials(2, 8) + 30 + sage: number_of_irreducible_polynomials(9, 9) + 43046640 + + This function is *much* faster than enumerating the polynomials:: + + sage: num = number_of_irreducible_polynomials(101, 99) + sage: num.bit_length() + 653 + + ALGORITHM: + + Classical formula `\frac1n \sum_{d\mid n} \mu(n/d) q^d` using the + Möbius function `\mu`; see :func:`moebius`. + """ + from sage.arith.misc import moebius + q, n = ZZ(q), ZZ(n) + r = ZZ.zero() + for d in n.divisors(): + r += moebius(n//d) * q**d + return r // n From 6702f8405bcd5acfe017c13f54397b3f4806de75 Mon Sep 17 00:00:00 2001 From: Lorenz Panny Date: Thu, 5 Oct 2023 13:27:15 +0200 Subject: [PATCH 3/6] support symbolic q in number_of_irreducible_polynomials() --- src/sage/combinat/q_analogues.py | 37 +++++++++++++++++++++----------- 1 file changed, 25 insertions(+), 12 deletions(-) diff --git a/src/sage/combinat/q_analogues.py b/src/sage/combinat/q_analogues.py index 3d84552c47a..58b5c6ba3c2 100644 --- a/src/sage/combinat/q_analogues.py +++ b/src/sage/combinat/q_analogues.py @@ -976,28 +976,36 @@ def q_stirling_number2(n, k, q=None): q_int(k, q=q) * q_stirling_number2(n - 1, k, q=q)) -def number_of_irreducible_polynomials(q, n): +def number_of_irreducible_polynomials(n, q=None): r""" - Return the number of irreducible polynomials of degree ``n`` - over the finite field with ``q`` elements. + Return the number of monic irreducible polynomials of degree ``n`` + over the finite field with ``q`` elements. If ``q`` is not given, + the result is returned as a polynomial in `\QQ[q]`. INPUT: - - ``q`` -- prime power - ``n`` -- positive integer + - ``q`` -- ``None`` (default) or a prime power OUTPUT: integer EXAMPLES:: - sage: number_of_irreducible_polynomials(2, 8) + sage: number_of_irreducible_polynomials(8, q=2) 30 - sage: number_of_irreducible_polynomials(9, 9) + sage: number_of_irreducible_polynomials(9, q=9) 43046640 + :: + + sage: poly = number_of_irreducible_polynomials(12); poly + 1/12*q^12 - 1/12*q^6 - 1/12*q^4 + 1/12*q^2 + sage: poly(5) == number_of_irreducible_polynomials(12, q=5) + True + This function is *much* faster than enumerating the polynomials:: - sage: num = number_of_irreducible_polynomials(101, 99) + sage: num = number_of_irreducible_polynomials(99, q=101) sage: num.bit_length() 653 @@ -1006,9 +1014,14 @@ def number_of_irreducible_polynomials(q, n): Classical formula `\frac1n \sum_{d\mid n} \mu(n/d) q^d` using the Möbius function `\mu`; see :func:`moebius`. """ + n = ZZ(n) + if n <= 0: + raise ValueError('n must be positive') + if q is None: + q = ZZ['q'].gen() + R = parent(q) from sage.arith.misc import moebius - q, n = ZZ(q), ZZ(n) - r = ZZ.zero() - for d in n.divisors(): - r += moebius(n//d) * q**d - return r // n + r = sum((moebius(n//d) * q**d for d in ZZ(n).divisors()), R.zero()) + if R is ZZ: + return r // n + return r / n From 2c9908c2a816f068f05760862dd11386b06a8175 Mon Sep 17 00:00:00 2001 From: Max Alekseyev Date: Mon, 5 Feb 2024 16:32:58 +0100 Subject: [PATCH 4/6] count irreducible multivariate polynomials, too --- src/doc/en/reference/references/index.rst | 10 +++++ src/sage/combinat/q_analogues.py | 45 +++++++++++++++++------ 2 files changed, 44 insertions(+), 11 deletions(-) diff --git a/src/doc/en/reference/references/index.rst b/src/doc/en/reference/references/index.rst index 09b78514e1c..0722536b0bb 100644 --- a/src/doc/en/reference/references/index.rst +++ b/src/doc/en/reference/references/index.rst @@ -47,6 +47,11 @@ REFERENCES: *Quantum compution with anyons: an F-matrix and braid calculator* (2022). https://arxiv.org/abs/2212.00831 +.. [Alekseyev2006] \M. Alekseyev: + (Forum post on counting irreducible multivariate polynomials), + 2006. + https://dxdy.ru/post7034.html + .. [AB2007] \M. Aschenbrenner, C. Hillar, *Finite generation of symmetric ideals*. Trans. Amer. Math. Soc. 359 (2007), no. 11, 5171--5192. @@ -503,6 +508,11 @@ REFERENCES: *A new construction for Hadamard matrices*. Bulletin of the American Mathematical Society 71(1):169-170, 1965. +.. [Bodin2007] \A. Bodin: + Number of irreducible polynomials in several variables over finite fields, + The American Mathematical Monthly 115(7), pp. 653-660, 2008. + https://arxiv.org/abs/0706.0157 + .. [BH2012] \A. Brouwer and W. Haemers, Spectra of graphs, Springer, 2012, diff --git a/src/sage/combinat/q_analogues.py b/src/sage/combinat/q_analogues.py index 58b5c6ba3c2..95a11233fbc 100644 --- a/src/sage/combinat/q_analogues.py +++ b/src/sage/combinat/q_analogues.py @@ -976,18 +976,21 @@ def q_stirling_number2(n, k, q=None): q_int(k, q=q) * q_stirling_number2(n - 1, k, q=q)) -def number_of_irreducible_polynomials(n, q=None): +def number_of_irreducible_polynomials(n, q=None, m=1): r""" Return the number of monic irreducible polynomials of degree ``n`` - over the finite field with ``q`` elements. If ``q`` is not given, - the result is returned as a polynomial in `\QQ[q]`. + in ``m`` variables over the finite field with ``q`` elements. + + If ``q`` is not given, the result is returned as an integer-valued + polynomial in `\QQ[q]`. INPUT: - ``n`` -- positive integer - ``q`` -- ``None`` (default) or a prime power + - ``m`` -- positive integer (default `1`) - OUTPUT: integer + OUTPUT: integer or integer-valued polynomial over `\QQ` EXAMPLES:: @@ -995,6 +998,8 @@ def number_of_irreducible_polynomials(n, q=None): 30 sage: number_of_irreducible_polynomials(9, q=9) 43046640 + sage: number_of_irreducible_polynomials(5, q=11, m=3) + 2079650567184059145647246367401741345157369643207055703168 :: @@ -1002,6 +1007,10 @@ def number_of_irreducible_polynomials(n, q=None): 1/12*q^12 - 1/12*q^6 - 1/12*q^4 + 1/12*q^2 sage: poly(5) == number_of_irreducible_polynomials(12, q=5) True + sage: poly = number_of_irreducible_polynomials(5, m=3); poly + q^55 + q^54 + q^53 + q^52 + q^51 + q^50 + ... + 1/5*q^5 - 1/5*q^3 - 1/5*q^2 - 1/5*q + sage: poly(11) == number_of_irreducible_polynomials(5, q=11, m=3) + True This function is *much* faster than enumerating the polynomials:: @@ -1011,17 +1020,31 @@ def number_of_irreducible_polynomials(n, q=None): ALGORITHM: - Classical formula `\frac1n \sum_{d\mid n} \mu(n/d) q^d` using the - Möbius function `\mu`; see :func:`moebius`. + In the univariate case, classical formula + `\frac1n \sum_{d\mid n} \mu(n/d) q^d` + using the Möbius function `\mu`; + see :func:`moebius`. + + In the multivariate case, formula from [Bodin2007]_, + independently [Alekseyev2006]_. """ n = ZZ(n) if n <= 0: raise ValueError('n must be positive') if q is None: - q = ZZ['q'].gen() + from sage.rings.rational_field import QQ + q = QQ['q'].gen() # for m > 1, we produce an integer-valued polynomial in q, but it does not necessarily have integer coefficients R = parent(q) - from sage.arith.misc import moebius - r = sum((moebius(n//d) * q**d for d in ZZ(n).divisors()), R.zero()) - if R is ZZ: + if m == 1: + from sage.arith.misc import moebius + r = sum((moebius(n//d) * q**d for d in n.divisors()), R.zero()) return r // n - return r / n + elif m > 1: + from sage.functions.other import binomial + from sage.combinat.partition import Partitions + r = [] + for d in range(n): + r.append( (q**binomial(d+m,m-1) - 1) // (q-1) * q**binomial(d+m,m) - sum(prod(binomial(r_+t-1,t) for r_,t in zip(r,p.to_exp(d))) for p in Partitions(d+1,max_part=d)) ) + return r[-1] + else: + raise ValueError('m must be positive') From 4935e12a289fbc543ad2529e6c2a9ab90eaabdcf Mon Sep 17 00:00:00 2001 From: Lorenz Panny Date: Wed, 21 Feb 2024 04:58:43 +0100 Subject: [PATCH 5/6] reviewer feedback --- src/sage/combinat/q_analogues.py | 42 +++++++++++++++++++++++--------- 1 file changed, 30 insertions(+), 12 deletions(-) diff --git a/src/sage/combinat/q_analogues.py b/src/sage/combinat/q_analogues.py index 95a11233fbc..4db8c47d324 100644 --- a/src/sage/combinat/q_analogues.py +++ b/src/sage/combinat/q_analogues.py @@ -1031,20 +1031,38 @@ def number_of_irreducible_polynomials(n, q=None, m=1): n = ZZ(n) if n <= 0: raise ValueError('n must be positive') + if m <= 0: + raise ValueError('m must be positive') + if q is None: from sage.rings.rational_field import QQ - q = QQ['q'].gen() # for m > 1, we produce an integer-valued polynomial in q, but it does not necessarily have integer coefficients - R = parent(q) + q = QQ['q'].gen() # we produce an integer-valued polynomial in q, but it does not necessarily have integer coefficients + if m == 1: from sage.arith.misc import moebius - r = sum((moebius(n//d) * q**d for d in n.divisors()), R.zero()) + r = sum((moebius(n//d) * q**d for d in n.divisors()), parent(q).zero()) return r // n - elif m > 1: - from sage.functions.other import binomial - from sage.combinat.partition import Partitions - r = [] - for d in range(n): - r.append( (q**binomial(d+m,m-1) - 1) // (q-1) * q**binomial(d+m,m) - sum(prod(binomial(r_+t-1,t) for r_,t in zip(r,p.to_exp(d))) for p in Partitions(d+1,max_part=d)) ) - return r[-1] - else: - raise ValueError('m must be positive') + + from sage.functions.other import binomial + from sage.combinat.partition import Partitions + + def monic_reducible(irreducible, d): + """ + Compute the number of monic reducible polynomials of degree `d` + given the numbers of irreducible polynomials up to degree `d-1`. + """ + res = 0 + for p in Partitions(d+1, max_part=d): + tmp = 1 + for r, t in zip(irreducible, p.to_exp(d)): + tmp *= binomial(r+t-1, t) + res += tmp + return res + + r = [] + for d in range(n): + monic = (q**binomial(d + m, m - 1) - 1) * q**binomial(d + m, m) // (q - 1) + reducible = monic_reducible(r, d) + r.append(monic - reducible) + + return r[-1] From d952887dff36f7ccd758e74898a6b394aa2fffe8 Mon Sep 17 00:00:00 2001 From: Lorenz Panny <84067835+yyyyx4@users.noreply.github.com> Date: Wed, 21 Feb 2024 16:46:16 +0100 Subject: [PATCH 6/6] reviewer suggestion Co-authored-by: Giacomo Pope <44242839+GiacomoPope@users.noreply.github.com> --- src/sage/combinat/q_analogues.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/sage/combinat/q_analogues.py b/src/sage/combinat/q_analogues.py index 4db8c47d324..f2b744d179f 100644 --- a/src/sage/combinat/q_analogues.py +++ b/src/sage/combinat/q_analogues.py @@ -1053,10 +1053,7 @@ def monic_reducible(irreducible, d): """ res = 0 for p in Partitions(d+1, max_part=d): - tmp = 1 - for r, t in zip(irreducible, p.to_exp(d)): - tmp *= binomial(r+t-1, t) - res += tmp + res += prod(binomial(r+t-1, t) for r, t in zip(irreducible, p.to_exp(d))) return res r = []