Skip to content

Commit

Permalink
gh-35334: Improvements to squarefree_decomposition() for finite fields.
Browse files Browse the repository at this point in the history
    
<!-- ^^^^^
Please provide a concise, informative and self-explanatory title.
Don't put issue numbers in there, do this in the PR body below.
For example, instead of "Fixes #1234" use "Introduce new method to
calculate 1+1"
-->
### 📚 Description

We make some optimizations to the computation of
`squarefree_decomposition()` for polynomials over finite fields. This is
a followup to #35323.

We make improvements by only getting the parent of `T0` once and
manually keep track of its degree.

The current branch
```python
sage: x = GF(4)["x"].gen()
sage: p = x^2 + 1
sage: %timeit p.squarefree_decomposition()
140 µs ± 22.1 µs per loop (mean ± std. dev. of 7 runs, 10,000 loops
each)
```
versus with #35323:
```python
168 µs ± 5.28 µs per loop (mean ± std. dev. of 7 runs, 10,000 loops
each)
```
versus 10.0.beta5:
```python
158 µs ± 6.74 µs per loop (mean ± std. dev. of 7 runs, 10,000 loops
each)
```

<!-- Describe your changes here in detail -->
<!-- Why is this change required? What problem does it solve? -->
<!-- If it resolves an open issue, please link to the issue here. For
example "Closes #1337" -->

### 📝 Checklist

<!-- Put an `x` in all the boxes that apply. -->
<!-- If your change requires a documentation PR, please link it
appropriately -->
<!-- If you're unsure about any of these, don't hesitate to ask. We're
here to help! -->

- [x] I have made sure that the title is self-explanatory and the
description concisely explains the PR.
- [x] I have linked an issue or discussion.
- [x] I have created tests covering the changes.
- [x] I have updated the documentation accordingly.

### ⌛ Dependencies

- #35323 Based on this change and needs to avoid conflicts.
    
URL: #35334
Reported by: Travis Scrimshaw
Reviewer(s): Rémy Oudompheng
  • Loading branch information
Release Manager committed Apr 4, 2023
2 parents 4134050 + fdc51c2 commit de75f8f
Showing 1 changed file with 24 additions and 17 deletions.
41 changes: 24 additions & 17 deletions src/sage/rings/finite_rings/finite_field_base.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -537,8 +537,9 @@ cdef class FiniteField(Field):

def _squarefree_decomposition_univariate_polynomial(self, f):
"""
Return the square-free decomposition of this polynomial. This is a
partial factorization into square-free, coprime polynomials.
Return the square-free decomposition of this polynomial.
This is a partial factorization into square-free, coprime polynomials.
This is a helper method for
:meth:`sage.rings.polynomial.squarefree_decomposition`.
Expand All @@ -547,8 +548,8 @@ cdef class FiniteField(Field):
- ``f`` -- a univariate non-zero polynomial over this field
ALGORITHM; [Coh1993]_, algorithm 3.4.2 which is basically the algorithm in
[Yun1976]_ with special treatment for powers divisible by `p`.
ALGORITHM: [Coh1993]_, Algorithm 3.4.2 which is basically the algorithm
in [Yun1976]_ with special treatment for powers divisible by `p`.
EXAMPLES::
Expand Down Expand Up @@ -591,49 +592,55 @@ cdef class FiniteField(Field):
if f.degree() == 0:
return Factorization([], unit=f[0])

factors = []
p = self.characteristic()
cdef Py_ssize_t i, k
cdef list factors = []
cdef Integer p = Integer(self.characteristic())
unit = f.leading_coefficient()
T0 = f.monic()
e = 1
if T0.degree() > 0:
cdef Integer e = Integer(1)
cdef Integer T0_deg = T0.degree()
if T0_deg > 0:
P = T0.parent()
der = T0.derivative()
pth_root = self.frobenius_endomorphism(-1)
while der.is_zero():
T0 = T0.parent()([pth_root(T0[p*i]) for i in range(T0.degree()//p + 1)])
T0 = P([pth_root(T0[p*i]) for i in range(T0_deg//p + 1)])
T0_deg //= p
if T0 == 1:
raise RuntimeError
der = T0.derivative()
e = e*p
e *= p
T = T0.gcd(der)
V = T0 // T
k = 0
while T0.degree() > 0:
while T0_deg > 0:
k += 1
if p.divides(k):
T = T // V
k += 1
W = V.gcd(T)
if W.degree() < V.degree():
factors.append((V // W, e*k))
factors.append((V // W, e * k))
V = W
T = T // V
if V.degree() == 0:
if T.degree() == 0:
break
# T is of the form sum_{i=0}^n t_i X^{pi}
T0 = T0.parent()([pth_root(T[p*i]) for i in range(T.degree()//p + 1)])
T0 = P([pth_root(T[p*i]) for i in range(T.degree()//p + 1)])
T0_deg //= p
der = T0.derivative()
e = p*e
e *= p
while der.is_zero():
T0 = T0.parent()([pth_root(T0[p*i]) for i in range(T0.degree()//p + 1)])
T0 = P([pth_root(T0[p*i]) for i in range(T0_deg//p + 1)])
T0_deg //= p
der = T0.derivative()
e = p*e
e *= p
T = T0.gcd(der)
V = T0 // T
k = 0
else:
T = T//V
T = T // V

return Factorization(factors, unit=unit, sort=False)

Expand Down

0 comments on commit de75f8f

Please sign in to comment.