Skip to content

Commit 4e338c6

Browse files
author
Release Manager
committed
gh-39402: Simplify __invert__ for polynomial quotient ring element Previously `__invert__` uses `xgcd`, while the method `inverse_mod` already exists, it's duplication of code. Besides, while simplifying this some missing features in `inverse_mod` is uncovered. ### 📝 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 created tests covering the changes. - [x] I have updated the documentation and checked the documentation preview. ### ⌛ Dependencies <!-- List all open PRs that this PR logically depends on. For example, --> <!-- - #12345: short description why this is a dependency --> <!-- - #34567: ... --> URL: #39402 Reported by: user202729 Reviewer(s):
2 parents 2369cad + 9f0b2c3 commit 4e338c6

File tree

3 files changed

+71
-34
lines changed

3 files changed

+71
-34
lines changed

src/sage/rings/polynomial/polynomial_element.pyx

Lines changed: 56 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1609,6 +1609,43 @@ cdef class Polynomial(CommutativePolynomial):
16091609
AUTHORS:
16101610
16111611
- Robert Bradshaw (2007-05-31)
1612+
1613+
TESTS:
1614+
1615+
At the time of writing :meth:`inverse_mod` is not implemented in general for the following ring,
1616+
but it should fallback to :meth:`inverse_of_unit` when possible::
1617+
1618+
sage: R.<u,v> = ZZ[]
1619+
sage: S = R.localization(u)
1620+
sage: T.<x> = S[]
1621+
sage: T
1622+
Univariate Polynomial Ring in x over Multivariate Polynomial Ring in u, v over Integer Ring localized at (u,)
1623+
sage: T(u).is_unit()
1624+
True
1625+
sage: T(u).inverse_of_unit()
1626+
1/u
1627+
sage: T(u).inverse_mod(T(v))
1628+
1/u
1629+
sage: T(v).inverse_mod(T(v^2-1))
1630+
Traceback (most recent call last):
1631+
...
1632+
NotImplementedError: Multivariate Polynomial Ring in u, v over Integer Ring localized at (u,) does not provide...
1633+
1634+
The behavior of ``xgcd`` over rings like ``ZZ`` are nonstandard, we check the behavior::
1635+
1636+
sage: R.<x> = ZZ[]
1637+
sage: a = 2*x^2+1
1638+
sage: b = -2*x^2+1
1639+
sage: a.inverse_mod(b)
1640+
Traceback (most recent call last):
1641+
...
1642+
NotImplementedError: The base ring (=Integer Ring) is not a field
1643+
sage: a.xgcd(b)
1644+
(16, 8, 8)
1645+
sage: a*x^2+b*(x^2+1)
1646+
1
1647+
sage: a*x^2%b # shows the result exists, thus we cannot raise ValueError, only NotImplementedError
1648+
1
16121649
"""
16131650
from sage.rings.ideal import Ideal_generic
16141651
if isinstance(m, Ideal_generic):
@@ -1626,13 +1663,25 @@ cdef class Polynomial(CommutativePolynomial):
16261663
return a.parent()(~u)
16271664
if a.parent().is_exact():
16281665
# use xgcd
1629-
g, s, _ = a.xgcd(m)
1630-
if g == 1:
1631-
return s
1632-
elif g.is_unit():
1633-
return g.inverse_of_unit() * s
1634-
else:
1635-
raise ValueError("Impossible inverse modulo")
1666+
try:
1667+
g, s, _ = a.xgcd(m)
1668+
if g == 1:
1669+
return s
1670+
elif g.is_unit():
1671+
return g.inverse_of_unit() * s
1672+
else:
1673+
R = m.base_ring()
1674+
if R.is_field():
1675+
raise ValueError("Impossible inverse modulo")
1676+
else:
1677+
raise NotImplementedError(f"The base ring (={R}) is not a field")
1678+
except NotImplementedError:
1679+
# attempt fallback, return inverse_of_unit() if this is already an unit
1680+
try:
1681+
return a.inverse_of_unit()
1682+
except (ArithmeticError, ValueError, NotImplementedError):
1683+
pass
1684+
raise
16361685
else:
16371686
# xgcd may not converge for inexact rings.
16381687
# Instead solve for the coefficients of

src/sage/rings/polynomial/polynomial_integer_dense_ntl.pyx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -619,7 +619,7 @@ cdef class Polynomial_integer_dense_ntl(Polynomial):
619619
since they need not exist. Instead, over the integers, we
620620
first multiply `g` by a divisor of the resultant of `a/g` and
621621
`b/g`, up to sign, and return ``g, u, v`` such that
622-
``g = s*self + s*right``. But note that this `g` may be a
622+
``g = u*self + v*right``. But note that this `g` may be a
623623
multiple of the gcd.
624624
625625
If ``self`` and ``right`` are coprime as polynomials over the

src/sage/rings/polynomial/polynomial_quotient_ring_element.py

Lines changed: 14 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -386,10 +386,6 @@ def __invert__(self):
386386
"""
387387
Return the inverse of this element.
388388
389-
.. WARNING::
390-
391-
Only implemented when the base ring is a field.
392-
393389
EXAMPLES::
394390
395391
sage: R.<x> = QQ[]
@@ -414,35 +410,27 @@ def __invert__(self):
414410
sage: (2*y)^(-1)
415411
Traceback (most recent call last):
416412
...
417-
NotImplementedError: The base ring (=Ring of integers modulo 16) is not a field
413+
NotImplementedError
414+
sage: (2*y+1)^(-1) # this cannot raise ValueError because...
415+
Traceback (most recent call last):
416+
...
417+
NotImplementedError
418+
sage: (2*y+1) * (10*y+5) # the element is in fact invertible
419+
1
418420
419421
Check that :issue:`29469` is fixed::
420422
421423
sage: ~S(3)
422424
11
423425
"""
424-
if self._polynomial.is_zero():
425-
raise ZeroDivisionError("element %s of quotient polynomial ring not invertible" % self)
426-
if self._polynomial.is_one():
427-
return self
428-
429-
parent = self.parent()
430-
426+
P = self.parent()
431427
try:
432-
if self._polynomial.is_unit():
433-
inv_pol = self._polynomial.inverse_of_unit()
434-
return parent(inv_pol)
435-
except (TypeError, NotImplementedError):
436-
pass
437-
438-
base = parent.base_ring()
439-
if not base.is_field():
440-
raise NotImplementedError("The base ring (=%s) is not a field" % base)
441-
g, _, a = parent.modulus().xgcd(self._polynomial)
442-
if g.degree() != 0:
443-
raise ZeroDivisionError("element %s of quotient polynomial ring not invertible" % self)
444-
c = g[0]
445-
return self.__class__(self.parent(), (~c)*a, check=False)
428+
return type(self)(P, self._polynomial.inverse_mod(P.modulus()), check=False)
429+
except ValueError as e:
430+
if e.args[0] == "Impossible inverse modulo":
431+
raise ZeroDivisionError(f"element {self} of quotient polynomial ring not invertible")
432+
else:
433+
raise NotImplementedError
446434

447435
def field_extension(self, names):
448436
r"""

0 commit comments

Comments
 (0)