Skip to content

Commit

Permalink
gh-38674: cythonize the chain polynomials of posets
Browse files Browse the repository at this point in the history
    
for the sake of speed

### 📝 Checklist

- [x] The title is concise and informative.
- [x] The description explains in detail what this PR is about.
- [ ] I have linked a relevant issue or discussion.
- [x] I have created tests covering the changes.
- [x] I have updated the documentation and checked the documentation
preview.
    
URL: #38674
Reported by: Frédéric Chapoton
Reviewer(s): David Coudert, Frédéric Chapoton
  • Loading branch information
Release Manager committed Sep 22, 2024
2 parents 26df4e9 + 6b50b66 commit f4305c7
Show file tree
Hide file tree
Showing 3 changed files with 67 additions and 13 deletions.
47 changes: 47 additions & 0 deletions src/sage/combinat/posets/hasse_cython_flint.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,53 @@ from sage.libs.flint.fmpz_mat cimport *
from sage.matrix.matrix_integer_dense cimport Matrix_integer_dense
from sage.matrix.matrix_space import MatrixSpace
from sage.rings.integer_ring import ZZ
from sage.libs.flint.fmpz_poly_sage cimport Fmpz_poly


cpdef Fmpz_poly chain_poly(list positions):
r"""
Return the chain polynomial of a poset.
INPUT:
- ``positions`` -- a list of sets of integers describing the poset, as
given by the lazy attribute ``_leq_storage`` of Hasse diagrams
OUTPUT: a Flint polynomial in one variable over `\ZZ`.
EXAMPLES::
sage: from sage.combinat.posets.hasse_cython_flint import chain_poly
sage: D = [{0, 1}, {1}]
sage: chain_poly(D)
3 1 2 1
sage: P = posets.TamariLattice(5)
sage: H = P._hasse_diagram
sage: D = H._leq_storage
sage: chain_poly(D)
12 1 42 357 1385 3133 4635 4758 3468 1778 612 127 12
"""
cdef Py_ssize_t n = len(positions)
cdef Py_ssize_t i, j

q = Fmpz_poly([0, 1])
zero = Fmpz_poly(0)
one = Fmpz_poly(1)

cdef list chain_polys = [zero] * n

# chain_polys[i] will be the generating function for the
# chains with lowest vertex i (in the labelling of the
# Hasse diagram).
for i in range(n - 1, -1, -1):
cpi = q
for j in positions[i]:
cpi += q * chain_polys[j]
chain_polys[i] = cpi
total = one
for i in range(n):
total += chain_polys[i]
return total


cpdef Matrix_integer_dense moebius_matrix_fast(list positions):
Expand Down
20 changes: 19 additions & 1 deletion src/sage/combinat/posets/hasse_diagram.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,8 @@
from sage.rings.integer_ring import ZZ

lazy_import('sage.combinat.posets.hasse_cython_flint',
['moebius_matrix_fast', 'coxeter_matrix_fast'])
['moebius_matrix_fast', 'coxeter_matrix_fast',
'chain_poly'])
lazy_import('sage.matrix.constructor', 'matrix')
lazy_import('sage.rings.finite_rings.finite_field_constructor', 'GF')

Expand Down Expand Up @@ -2413,6 +2414,23 @@ def chains(self, element_class=list, exclude=None, conversion=None):
"""
return IncreasingChains(self._leq_storage, element_class, exclude, conversion)

def chain_polynomial(self):
"""
Return the chain polynomial of the poset.
The coefficient of `q^k` is the number of chains of `k`
elements in the poset. List of coefficients of this polynomial
is also called a *f-vector* of the poset.
EXAMPLES::
sage: P = posets.ChainPoset(3)
sage: H = P._hasse_diagram
sage: t = H.chain_polynomial(); t
q^3 + 3*q^2 + 3*q + 1
"""
return chain_poly(self._leq_storage)._sage_('q') # noqa: F821

def is_linear_interval(self, t_min, t_max) -> bool:
"""
Return whether the interval ``[t_min, t_max]`` is linear.
Expand Down
13 changes: 1 addition & 12 deletions src/sage/combinat/posets/posets.py
Original file line number Diff line number Diff line change
Expand Up @@ -7682,18 +7682,7 @@ def chain_polynomial(self):
sage: R.chain_polynomial()
q + 1
"""
hasse = self._hasse_diagram
q = polygen(ZZ, 'q')
one = q.parent().one()
hasse_size = hasse.cardinality()
chain_polys = [0] * hasse_size
# chain_polys[i] will be the generating function for the
# chains with topmost vertex i (in the labelling of the
# Hasse diagram).
for i in range(hasse_size):
chain_polys[i] = q + sum(q * chain_polys[j]
for j in hasse.principal_order_ideal(i))
return one + sum(chain_polys)
return self._hasse_diagram.chain_polynomial()

def order_polynomial(self):
r"""
Expand Down

0 comments on commit f4305c7

Please sign in to comment.