Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 1 addition & 3 deletions src/doc/en/thematic_tutorials/coercion_and_categories.rst
Original file line number Diff line number Diff line change
Expand Up @@ -105,8 +105,7 @@ Let us nevertheless provide an example using::
as this base class still provides a few more methods than a general parent::

sage: [p for p in dir(Field) if p not in dir(Parent)]
['_CommutativeRing__fraction_field',
'__iter__',
['__iter__',
'__len__',
'__rxor__',
'__xor__',
Expand All @@ -121,7 +120,6 @@ as this base class still provides a few more methods than a general parent::
'_zero_element',
'base_extend',
'extension',
'fraction_field',
'gen',
'gens',
'ngens',
Expand Down
5 changes: 2 additions & 3 deletions src/sage/algebras/splitting_algebra.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@
from sage.misc.cachefunc import cached_method
from sage.rings.polynomial.polynomial_quotient_ring import PolynomialQuotientRing_domain
from sage.rings.polynomial.polynomial_quotient_ring_element import PolynomialQuotientRingElement
from sage.structure.category_object import normalize_names


# -------------------------------------------------------------------------
Expand Down Expand Up @@ -221,12 +222,11 @@
# checking input parameters
# ---------------------------------------------------------------

base_ring = monic_polynomial.base_ring()
if not monic_polynomial.is_monic():
raise ValueError("given polynomial must be monic")
deg = monic_polynomial.degree()

from sage.structure.category_object import normalize_names
base_ring = monic_polynomial.base_ring()
self._root_names = normalize_names(deg - 1, names)
root_names = list(self._root_names)
verbose("Create splitting algebra to base ring %s and polynomial %s (%s %s)"
Expand Down Expand Up @@ -374,7 +374,6 @@
invert_items = list(self._invertible_elements.items())
for k, v in invert_items:
self._invertible_elements.update({v: k})
return

########################################################################
# ----------------------------------------------------------------------
Expand All @@ -400,7 +399,7 @@
False,
False))

sage: TestSuite(S).run()

Check failure on line 402 in src/sage/algebras/splitting_algebra.py

View workflow job for this annotation

GitHub Actions / Conda (ubuntu, Python 3.12, new)

Failed example:

Failed example:: Got: Failure in _test_fraction_field: Traceback (most recent call last): File "sage/structure/parent.pyx", line 894, in sage.structure.parent.Parent.__call__ mor = <map.Map> self._convert_from_hash.get(R) File "sage/structure/coerce_dict.pyx", line 650, in sage.structure.coerce_dict.MonoDict.get raise KeyError(k) KeyError: Univariate Polynomial Ring in Z over Factorization Algebra of x^4 - t*x^3 - u*x^2 - v*x + w with roots [Y] over Multivariate Laurent Polynomial Ring in t, u, v, w over Integer Ring During handling of the above exception, another exception occurred: Traceback (most recent call last): File "sage/structure/parent.pyx", line 2496, in sage.structure.parent.Parent._internal_convert_map_from return self._convert_from_hash.get(S) File "sage/structure/coerce_dict.pyx", line 650, in sage.structure.coerce_dict.MonoDict.get raise KeyError(k) KeyError: Univariate Polynomial Ring in Z over Factorization Algebra of x^4 - t*x^3 - u*x^2 - v*x + w with roots [Y] over Multivariate Laurent Polynomial Ring in t, u, v, w over Integer Ring During handling of the above exception, another exception occurred: Traceback (most recent call last): File "/usr/share/miniconda/envs/sage-dev/lib/python3.12/site-packages/sage/misc/sage_unittest.py", line 300, in run test_method(tester=tester) File "/usr/share/miniconda/envs/sage-dev/lib/python3.12/site-packages/sage/categories/integral_domains.py", line 203, in _test_fraction_field fraction_field = self.fraction_field() ^^^^^^^^^^^^^^^^^^^^^ File "/usr/share/miniconda/envs/sage-dev/lib/python3.12/site-packages/sage/rings/polynomial/polynomial_quotient_ring.py", line 1283, in fraction_field return self.base().change_ring(frac).quo(self.modulus(), ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/share/miniconda/envs/sage-dev/lib/python3.12/site-packages/sage/categories/rings.py", line 1258, in quo return self.quotient(I, names=names, **kwds) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/share/miniconda/envs/sage-dev/lib/python3.12/site-packages/sage/categories/rings.py", line 1203, in quotient return QuotientRing(self, I, names=names, **kwds) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/share/miniconda/envs/sage-dev/lib/python3.12/site-packages/sage/rings/quotient_ring.py", line 318, in QuotientRing I = R.ideal(I) ^^^^^^^^^^ File "/usr/share/miniconda/envs/sage-dev/lib/python3.12/site-packages/sage/categories/rings.py", line 1106, in ideal gens = [self(g) for g in gens] ^^^^^^^ File "sage/structure/parent.pyx", line 896, in sage.structure.parent.Parent.__call__ mor = <map.Map> self._internal_convert_map_from(R) File "sage/structure/parent.pyx", line 2498, in sage.structure.parent.Parent._internal_convert_map_from mor = self.discover_convert_map_from(S) File "sage/structure/parent.pyx", line 2514, in sage.structure.parent.Parent.discover_convert_map_from cdef map.Map mor = self._internal_coerce_map_from(S) File "sage/structure/parent.pyx", line 2234, in sage.structure.parent.Parent._internal_coerce_map_from mor = self.discover_coerce_map_from(S) File "sage/structure/parent.pyx", line 2378, in sage.structure.parent.Parent.discover_coerce_map_from user_provided_mor = self._coerce_map_from_(S) File "sage/structure/parent_old.pyx", line 241, in sage.structure.parent_old.Parent._coerce_map_from_ cpdef _coerce_map_from_(self, S): File "/usr/share/miniconda/envs/sage-dev/lib/python3.12/site-packages/sage/rings/polynomial/polynomial_ring.py", line 878, in _coerce_map_from_ f = base_ring.coerce_map_from(P.base_ring()) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "sage/structure/parent.pyx", line 2104, in sage.structure.parent.Parent.coerce_map_from cpdef coerce_map_from(self, S): File "sage/structure/parent.pyx", line 2140, in sage.structure.parent.Parent.coerce_map_from return copy(self._internal_coerce_map_from(S)) File "sage/structure/parent.pyx", li
"""
def_polynomial = self.defining_polynomial()
def_coefficients = self._coefficients_list[0]
Expand Down Expand Up @@ -475,7 +474,7 @@
u + v
sage: S(X*Y + X)
X*Y + X
sage: TestSuite(S).run() # indirect doctest

Check failure on line 477 in src/sage/algebras/splitting_algebra.py

View workflow job for this annotation

GitHub Actions / Conda (ubuntu, Python 3.12, new)

Failed example:

Failed example:: Got: Failure in _test_fraction_field: Traceback (most recent call last): File "/usr/share/miniconda/envs/sage-dev/lib/python3.12/site-packages/sage/misc/sage_unittest.py", line 300, in run test_method(tester=tester) File "/usr/share/miniconda/envs/sage-dev/lib/python3.12/site-packages/sage/categories/integral_domains.py", line 206, in _test_fraction_field fraction_field.coerce(x) File "sage/structure/parent.pyx", line 1190, in sage.structure.parent.Parent.coerce cpdef coerce(self, x): File "sage/structure/parent.pyx", line 1213, in sage.structure.parent.Parent.coerce mor = self._internal_coerce_map_from(R) File "sage/structure/parent.pyx", line 2234, in sage.structure.parent.Parent._internal_coerce_map_from mor = self.discover_coerce_map_from(S) File "sage/structure/parent.pyx", line 2378, in sage.structure.parent.Parent.discover_coerce_map_from user_provided_mor = self._coerce_map_from_(S) File "sage/structure/parent_old.pyx", line 241, in sage.structure.parent_old.Parent._coerce_map_from_ cpdef _coerce_map_from_(self, S): File "/usr/share/miniconda/envs/sage-dev/lib/python3.12/site-packages/sage/rings/polynomial/polynomial_quotient_ring.py", line 594, in _coerce_map_from_ if self.__ring.has_coerce_map_from(R): ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "sage/structure/parent.pyx", line 2063, in sage.structure.parent.Parent.has_coerce_map_from cpdef bint has_coerce_map_from(self, S) except -2: File "sage/structure/parent.pyx", line 2085, in sage.structure.parent.Parent.has_coerce_map_from return self._internal_coerce_map_from(S) is not None File "sage/structure/parent.pyx", line 2234, in sage.structure.parent.Parent._internal_coerce_map_from mor = self.discover_coerce_map_from(S) File "sage/structure/parent.pyx", line 2423, in sage.structure.parent.Parent.discover_coerce_map_from connecting = D._internal_coerce_map_from(S) File "sage/structure/parent.pyx", line 2234, in sage.structure.parent.Parent._internal_coerce_map_from mor = self.discover_coerce_map_from(S) File "sage/structure/parent.pyx", line 2378, in sage.structure.parent.Parent.discover_coerce_map_from user_provided_mor = self._coerce_map_from_(S) File "sage/structure/parent_old.pyx", line 241, in sage.structure.parent_old.Parent._coerce_map_from_ cpdef _coerce_map_from_(self, S): File "/usr/share/miniconda/envs/sage-dev/lib/python3.12/site-packages/sage/rings/polynomial/polynomial_quotient_ring.py", line 598, in _coerce_map_from_ if not self.__polynomial.divides(R.modulus()): ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "sage/structure/element.pyx", line 4579, in sage.structure.element.coerce_binop.new_method a, b = coercion_model.canonical_coercion(self, other) File "sage/structure/coerce.pyx", line 1434, in sage.structure.coerce.CoercionModel.canonical_coercion raise TypeError("no common canonical parent for objects with parents: '%s' and '%s'" % (xp, yp)) TypeError: no common canonical parent for objects with parents: 'Univariate Polynomial Ring in X over Fraction Field of Multivariate Polynomial Ring in u, v, w over Integer Ring' and 'Univariate Polynomial Ring in Y over Factorization Algebra of x^3 - u*x^2 + v*x - w with roots [X] over Multivariate Laurent Polynomial Ring in u, v, w over Integer Ring' ------------------------------------------------------------ The following tests failed: _test_fraction_field
"""
if isinstance(x, SplittingAlgebraElement):
# coercion from covering fixes pickling problems
Expand Down
30 changes: 29 additions & 1 deletion src/sage/categories/commutative_rings.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@

from sage.categories.category_with_axiom import CategoryWithAxiom
from sage.categories.cartesian_product import CartesianProductsCategory
from sage.misc.cachefunc import cached_method
from sage.structure.sequence import Sequence
from sage.structure.element import coercion_model

Expand Down Expand Up @@ -561,7 +562,7 @@ def _pseudo_fraction_field(self):
sage: Integers(15).fraction_field()
Traceback (most recent call last):
...
TypeError: self must be an integral domain.
TypeError: self must be an integral domain

TESTS::

Expand All @@ -575,6 +576,33 @@ def _pseudo_fraction_field(self):
except (NotImplementedError, TypeError):
return coercion_model.division_parent(self)

@cached_method
def fraction_field(self):
"""
Return the fraction field of ``self``.

EXAMPLES::

sage: R = Integers(389)['x,y']
sage: Frac(R)
Fraction Field of Multivariate Polynomial Ring in x, y over Ring of integers modulo 389
sage: R.fraction_field()
Fraction Field of Multivariate Polynomial Ring in x, y over Ring of integers modulo 389
"""
# from sage.categories.fields import Fields
from sage.categories.integral_domains import IntegralDomains
try:
if self.is_field():
# self in Fields(): would turn SR into a field !
return self
except NotImplementedError:
pass
if self not in IntegralDomains():
raise TypeError("self must be an integral domain")

import sage.rings.fraction_field
return sage.rings.fraction_field.FractionField_generic(self)

class ElementMethods:
def is_square(self, root=False):
"""
Expand Down
27 changes: 18 additions & 9 deletions src/sage/categories/integral_domains.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@
# Distributed under the terms of the GNU General Public License (GPL)
# https://www.gnu.org/licenses/
# *****************************************************************************

from sage.misc.cachefunc import cached_method
from sage.misc.lazy_import import lazy_import
from sage.misc.lazy_attribute import lazy_class_attribute
from sage.categories.category_with_axiom import CategoryWithAxiom
Expand Down Expand Up @@ -174,6 +174,22 @@ def localization(self, additional_units, names=None, normalize=True, category=No
from sage.rings.localization import Localization
return Localization(self, additional_units, names=names, normalize=normalize, category=category)

@cached_method
def fraction_field(self):
"""
Return the fraction field of ``self``.

EXAMPLES::

sage: R = GF(61)['x,y']
sage: Frac(R)
Fraction Field of Multivariate Polynomial Ring in x, y over Finite Field of size 61
sage: R.fraction_field()
Fraction Field of Multivariate Polynomial Ring in x, y over Finite Field of size 61
"""
import sage.rings.fraction_field
return sage.rings.fraction_field.FractionField_generic(self)

def _test_fraction_field(self, **options):
r"""
Test that the fraction field, if it is implemented, works
Expand All @@ -184,14 +200,7 @@ def _test_fraction_field(self, **options):
sage: ZZ._test_fraction_field()
"""
tester = self._tester(**options)
try:
fraction_field = self.fraction_field()
except (AttributeError, ImportError):
# some integral domains do not implement fraction_field() yet
if self in Fields():
raise
return

fraction_field = self.fraction_field()
for x in tester.some_elements():
# check that we can coerce into the fraction field
fraction_field.coerce(x)
Expand Down
2 changes: 1 addition & 1 deletion src/sage/categories/rings.py
Original file line number Diff line number Diff line change
Expand Up @@ -398,7 +398,7 @@ def is_integral_domain(self, proof=True) -> bool:
sage: R.fraction_field() # needs sage.libs.pari
Traceback (most recent call last):
...
TypeError: self must be an integral domain.
TypeError: self must be an integral domain
sage: R.is_integral_domain() # needs sage.libs.pari
False

Expand Down
2 changes: 1 addition & 1 deletion src/sage/combinat/sf/sf.py
Original file line number Diff line number Diff line change
Expand Up @@ -863,7 +863,7 @@ def __init__(self, R):

sage: Sym1 = SymmetricFunctions(FiniteField(23))
sage: Sym2 = SymmetricFunctions(Integers(23))
sage: TestSuite(Sym).run()
sage: TestSuite(Sym).run(skip="_test_fraction_field")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sym has a (very naive) implementation of a fraction field, which is actually used (in the lazy code). I missed what's going wrong, sorry.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fractions field of realizations are working fine. What is not working is the interplay between the Sym without realization and the fraction field tests.

"""
# change the line below to assert(R in Rings()) once MRO issues from #15536, #15475 are resolved
assert R in Fields() or R in Rings() # side effect of this statement assures MRO exists for R
Expand Down
2 changes: 1 addition & 1 deletion src/sage/rings/finite_rings/integer_mod_ring.py
Original file line number Diff line number Diff line change
Expand Up @@ -791,7 +791,7 @@ def _pseudo_fraction_field(self):
sage: Integers(15).fraction_field()
Traceback (most recent call last):
...
TypeError: self must be an integral domain.
TypeError: self must be an integral domain
sage: Integers(15)._pseudo_fraction_field()
Ring of integers modulo 15
sage: R.<x> = Integers(15)[]
Expand Down
14 changes: 14 additions & 0 deletions src/sage/rings/lazy_series_ring.py
Original file line number Diff line number Diff line change
Expand Up @@ -2754,7 +2754,7 @@
sage: L = LazyPowerSeriesRing(Zmod(6), 't')
sage: TestSuite(L).run(skip=['_test_revert'])
sage: L = LazyPowerSeriesRing(Zmod(6), 's, t')
sage: TestSuite(L).run(skip=['_test_revert'])

Check warning on line 2757 in src/sage/rings/lazy_series_ring.py

View workflow job for this annotation

GitHub Actions / Conda (macos, Python 3.13, all)

Warning: slow doctest:

slow doctest:: Test ran for 5.28s cpu, 8.77s wall Check ran for 0.00s cpu, 0.00s wall

sage: L = LazyPowerSeriesRing(QQ['q'], 't')
sage: TestSuite(L).run(skip='_test_fraction_field')
Expand Down Expand Up @@ -4194,6 +4194,20 @@
c = self.base_ring().an_element()
return self.element_class(self, Stream_exact([], constant=c, order=4))

def ngens(self):
r"""
Return the number of generators of ``self``.

This is always 1.

EXAMPLES::

sage: L = LazyDirichletSeriesRing(ZZ, 'z')
sage: L.ngens()
1
"""
return 1

Comment on lines +4197 to +4210
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure whether this is a good idea - I don't think the Dirichlet series ring is finitely generated.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well, no, but you have one generator t somewhere in the code..

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually, we don't. There is a data structure, which we carefully hide - the _internal_poly_ring.

Why is adding ngens necessary or useful? Maybe it helps me to understand or we find a better solution.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well, I am currently investigating the other failures.

I had to do that change in Lazy Dirichlet for their fraction field to work.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here is the failure without my fix

File "src/sage/rings/lazy_series_ring.py", line 3916, in sage.rings.lazy_series_ring.LazyDirichletSeriesRing.__init__
Failed example:
    TestSuite(L).run()                                                    # needs sage.symbolic
Expected nothing
Got:
    Failure in _test_fraction_field:
    Traceback (most recent call last):
      File "sage/structure/category_object.pyx", line 857, in sage.structure.category_object.CategoryObject.getattr_from_category
        return self._cached_methods[name]
    KeyError: 'ngens'
    <BLANKLINE>
    During handling of the above exception, another exception occurred:
    <BLANKLINE>
    Traceback (most recent call last):
      File "/home/chapoton/sage/src/sage/misc/sage_unittest.py", line 298, in run
        test_method(tester=tester)
      File "/home/chapoton/sage/src/sage/categories/integral_domains.py", line 203, in _test_fraction_field
        fraction_field = self.fraction_field()
                         ^^^^^^^^^^^^^^^^^^^^^
      File "sage/misc/cachefunc.pyx", line 2333, in sage.misc.cachefunc.CachedMethodCallerNoArgs.__call__
        self.cache = f(self._instance)
      File "/home/chapoton/sage/src/sage/categories/integral_domains.py", line 191, in fraction_field
        return sage.rings.fraction_field.FractionField_generic(self)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
      File "/home/chapoton/sage/src/sage/rings/fraction_field.py", line 205, in __init__
        Parent.__init__(self, base=R, names=R._names, category=cat)
      File "sage/structure/parent.pyx", line 301, in sage.structure.parent.Parent.__init__
        self._assign_names(names, normalize)
      File "sage/structure/parent_gens.pyx", line 152, in sage.structure.parent_gens.ParentWithGens._assign_names
        names = category_object.normalize_names(self.ngens(), names)
      File "/home/chapoton/sage/src/sage/rings/fraction_field.py", line 937, in ngens
        return self._R.ngens()
               ^^^^^^^^^^^^^
      File "sage/structure/category_object.pyx", line 851, in sage.structure.category_object.CategoryObject.__getattr__
        return self.getattr_from_category(name)
      File "sage/structure/category_object.pyx", line 866, in sage.structure.category_object.CategoryObject.getattr_from_category
        attr = getattr_from_other_class(self, cls, name)
      File "sage/cpython/getattr.pyx", line 358, in sage.cpython.getattr.getattr_from_other_class
        raise AttributeError(dummy_error_message)
    AttributeError: 'LazyDirichletSeriesRing_with_category' object has no attribute 'ngens'
    ------------------------------------------------------------
    The following tests failed: _test_fraction_field

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you know under which assumptions FractionField_generic is supposed to work?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here is something that causes problems :

sage: L = LazyDirichletSeriesRing(ZZ,'z')
sage: L._names
('z',)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry, I don't understand. Why is this causing problems?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well, this name is transmitted to the fraction field, which then tries to call "ngens", which forces us to add the silly "ngens" method

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So I think for the moment, the simplest way out is just to add the silly method "ngens". I have more difficult issues to fix, in particular something about src/sage/algebras/splitting_algebra.py

def some_elements(self):
"""
Return a list of elements of ``self``.
Expand Down
15 changes: 3 additions & 12 deletions src/sage/rings/padics/padic_valuation.py
Original file line number Diff line number Diff line change
Expand Up @@ -1406,26 +1406,17 @@ def _fraction_field(ring):
r"""
Return a fraction field of ``ring``.

EXAMPLES:
This workaround is no longer needed.

This works around some annoyances with ``ring.fraction_field()``::
EXAMPLES::

sage: R.<x> = ZZ[]
sage: S = R.quo(x^2 + 1)
sage: S.fraction_field()
Fraction Field of Univariate Quotient Polynomial Ring in xbar over Integer Ring with modulus x^2 + 1
Univariate Quotient Polynomial Ring in xbar over Rational Field with modulus x^2 + 1

sage: from sage.rings.padics.padic_valuation import _fraction_field
sage: _fraction_field(S)
Univariate Quotient Polynomial Ring in xbar over Rational Field with modulus x^2 + 1
"""
from sage.categories.fields import Fields
if ring in Fields():
return ring

from sage.rings.polynomial.polynomial_quotient_ring import PolynomialQuotientRing_generic
if isinstance(ring, PolynomialQuotientRing_generic):
from sage.categories.integral_domains import IntegralDomains
if ring in IntegralDomains():
return ring.base().change_ring(ring.base_ring().fraction_field()).quo(ring.modulus())
return ring.fraction_field()
4 changes: 2 additions & 2 deletions src/sage/rings/polynomial/multi_polynomial_libsingular.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -2320,12 +2320,12 @@ cdef class MPolynomial_libsingular(MPolynomial_libsingular_base):
sage: x/P(3)
Traceback (most recent call last):
...
TypeError: self must be an integral domain.
TypeError: self must be an integral domain

sage: x/3
Traceback (most recent call last):
...
TypeError: self must be an integral domain.
TypeError: self must be an integral domain

TESTS::

Expand Down
64 changes: 45 additions & 19 deletions src/sage/rings/polynomial/polynomial_quotient_ring.py
Original file line number Diff line number Diff line change
Expand Up @@ -171,7 +171,7 @@ class PolynomialQuotientRingFactory(UniqueFactory):
sage: R.quotient_by_principal_ideal(f)
Univariate Quotient Polynomial Ring in xbar over Rational Field with modulus x^2 - 1
"""
def create_key(self, ring, polynomial, names=None):
def create_key(self, ring, polynomial, names=None, category=None):
r"""
Return a unique description of the quotient ring specified by the
arguments.
Expand All @@ -180,15 +180,15 @@ def create_key(self, ring, polynomial, names=None):

sage: R.<x> = QQ[]
sage: PolynomialQuotientRing.create_key(R, x + 1)
(Univariate Polynomial Ring in x over Rational Field, x + 1, ('xbar',))
(Univariate Polynomial Ring in x over Rational Field, x + 1, ('xbar',), None)

TESTS:

We do not normalize the modulus even though we could divide out the
leading coefficient here::

sage: PolynomialQuotientRing.create_key(R, 2*x + 2)
(Univariate Polynomial Ring in x over Rational Field, 2*x + 2, ('xbar',))
(Univariate Polynomial Ring in x over Rational Field, 2*x + 2, ('xbar',), None)

Consequently, you get two distinct objects::

Expand Down Expand Up @@ -223,7 +223,7 @@ def create_key(self, ring, polynomial, names=None):
else:
names = normalize_names(ring.ngens(), names)

return ring, polynomial, names
return ring, polynomial, names, category

def create_object(self, version, key):
r"""
Expand All @@ -233,26 +233,26 @@ def create_object(self, version, key):

sage: R.<x> = QQ[]
sage: PolynomialQuotientRing.create_object((8, 0, 0),
....: (R, x^2 - 1, ('xbar')))
....: (R, x^2 - 1, ('xbar'), None))
Univariate Quotient Polynomial Ring in xbar over Rational Field with modulus x^2 - 1
"""
ring, polynomial, names = key
ring, polynomial, names, category = key

R = ring.base_ring()
from sage.categories.fields import Fields
from sage.categories.integral_domains import IntegralDomains
if R in IntegralDomains():
try:
is_irreducible = polynomial.is_irreducible()
except NotImplementedError: # is_irreducible sometimes not implemented
except NotImplementedError: # is_irreducible sometimes not implemented
pass
else:
if is_irreducible:
if R in Fields():
return PolynomialQuotientRing_field(ring, polynomial, names)
return PolynomialQuotientRing_field(ring, polynomial, names, category=category)
else:
return PolynomialQuotientRing_domain(ring, polynomial, names)
return PolynomialQuotientRing_generic(ring, polynomial, names)
return PolynomialQuotientRing_domain(ring, polynomial, names, category=category)
return PolynomialQuotientRing_generic(ring, polynomial, names, category=category)


PolynomialQuotientRing = PolynomialQuotientRingFactory("PolynomialQuotientRing")
Expand Down Expand Up @@ -434,14 +434,16 @@ def __init__(self, ring, polynomial, name=None, category=None):

self.__ring = ring
self.__polynomial = polynomial
category = CommutativeAlgebras(ring.base_ring().category()).Quotients().or_subcategory(category)
cat = CommutativeAlgebras(ring.base_ring().category()).Quotients()
if category is not None:
cat &= category
if self.is_finite():
# We refine the category for finite quotients.
# Note that is_finite() is cheap so it does not seem to do a lazy
# _refine_category_() in is_finite() as we do for is_field()
category = category.Finite()
cat = cat.Finite()

QuotientRing_generic.__init__(self, ring, ring.ideal(polynomial), names=name, category=category)
QuotientRing_generic.__init__(self, ring, ring.ideal(polynomial), names=name, category=cat)
self._base = ring # backwards compatibility -- different from QuotientRing_generic

_ideal_class_ = QuotientRing_generic._ideal_class_
Expand Down Expand Up @@ -532,9 +534,9 @@ def _element_constructor_(self, x):
"""
if not isinstance(x, str):
try:
return self.element_class(self, self.__ring(x) , check=True)
except TypeError:
xlift = getattr(x,'lift',None)
return self.element_class(self, self.__ring(x), check=True)
except (TypeError, ValueError):
xlift = getattr(x, 'lift', None)
if xlift is not None: # duck typing for quotient ring elements
return self.element_class(self, self.__ring(x.lift()), check=False)
# The problem with the string representation is that it could in principle
Expand Down Expand Up @@ -595,7 +597,7 @@ def _coerce_map_from_(self, R):
try:
if not self.__polynomial.divides(R.modulus()):
return False
except (ZeroDivisionError,ArithmeticError):
except (ZeroDivisionError, ArithmeticError):
return False
from sage.categories.homset import Hom
parent = Hom(R, self, category=self.category()._meet_(R.category()))
Expand Down Expand Up @@ -1259,6 +1261,28 @@ def polynomial_ring(self):

cover_ring = polynomial_ring

def fraction_field(self):
"""
Return the fraction field of ``self``.

EXAMPLES::

sage: R.<x> = ZZ[]
sage: S = R.quo(x^2 + 1)
sage: S.fraction_field()
Univariate Quotient Polynomial Ring in xbar over Rational Field with modulus x^2 + 1
"""
from sage.categories.fields import Fields
from sage.categories.integral_domains import IntegralDomains
if self in Fields():
return self
if self not in IntegralDomains():
raise TypeError("self must be an integral domain")

frac = self.base_ring().fraction_field()
return self.base().change_ring(frac).quo(self.modulus(),
category=Fields())

def random_element(self, degree=None, *args, **kwds):
"""
Return a random element of this quotient ring.
Expand Down Expand Up @@ -2317,8 +2341,10 @@ def __init__(self, ring, polynomial, name=None, category=None):
sage: h.parent() is H
True
"""
category = CommutativeAlgebras(ring.base_ring().category()).Quotients().NoZeroDivisors().or_subcategory(category)
PolynomialQuotientRing_generic.__init__(self, ring, polynomial, name, category)
cat = CommutativeAlgebras(ring.base_ring().category()).Quotients().NoZeroDivisors()
if category is not None:
cat &= category
PolynomialQuotientRing_generic.__init__(self, ring, polynomial, name, cat)

def field_extension(self, names):
r"""
Expand Down
Loading
Loading