Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

let the category setup handle the ideals #38821

Open
wants to merge 2 commits into
base: develop
Choose a base branch
from
Open
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
1 change: 0 additions & 1 deletion src/doc/en/thematic_tutorials/coercion_and_categories.rst
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,6 @@ This base class provides a lot more methods than a general parent::
'fraction_field',
'gen',
'gens',
'ideal',
'integral_closure',
'is_commutative',
'is_field',
Expand Down
20 changes: 20 additions & 0 deletions src/sage/categories/fields.py
Original file line number Diff line number Diff line change
Expand Up @@ -458,6 +458,26 @@
"""
return self

def ideal(self, *gens, **kwds):
"""
Return the ideal generated by gens.

EXAMPLES::

sage: QQ.ideal(2)
Principal ideal (1) of Rational Field
sage: QQ.ideal(0)
Principal ideal (0) of Rational Field
"""
if len(gens) == 1 and isinstance(gens[0], (list, tuple)):
gens = gens[0]
if not isinstance(gens, (list, tuple)):
gens = [gens]

Check warning on line 475 in src/sage/categories/fields.py

View check run for this annotation

Codecov / codecov/patch

src/sage/categories/fields.py#L475

Added line #L475 was not covered by tests
for x in gens:
if not self(x).is_zero():
return self.unit_ideal()
return self.zero_ideal()

def _squarefree_decomposition_univariate_polynomial(self, f):
r"""
Return the square-free decomposition of ``f`` over this field.
Expand Down
98 changes: 65 additions & 33 deletions src/sage/categories/rings.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,14 @@
# https://www.gnu.org/licenses/
# *****************************************************************************
from functools import reduce
from types import GeneratorType

from sage.misc.cachefunc import cached_method
from sage.misc.lazy_import import LazyImport
from sage.categories.category_with_axiom import CategoryWithAxiom
from sage.categories.rngs import Rngs
from sage.structure.element import Element
from sage.structure.parent import Parent


class Rings(CategoryWithAxiom):
Expand Down Expand Up @@ -815,32 +817,34 @@
"""
Create an ideal of this ring.

.. NOTE::
INPUT:

The code is copied from the base class
:class:`~sage.rings.ring.Ring`. This is
because there are rings that do not inherit
from that class, such as matrix algebras.
See :issue:`7797`.
- an element or a list/tuple/sequence of elements, the generators

INPUT:
- ``coerce`` -- boolean (default: ``True``); whether to first coerce
the elements into this ring. This must be a keyword
argument. Only set it to ``False`` if you are certain that each
generator is already in the ring.

- an element or a list/tuple/sequence of elements
- ``coerce`` -- boolean (default: ``True``);
first coerce the elements into this ring
- ``side`` -- (optional) string, one of ``'twosided'``
(default), ``'left'``, ``'right'``; determines
whether the resulting ideal is twosided, a left
ideal or a right ideal
- ``ideal_class`` -- callable (default: ``self._ideal_class_()``);
this must be a keyword argument. A constructor for ideals, taking
the ring as the first argument and then the generators.
Usually a subclass of :class:`~sage.rings.ideal.Ideal_generic` or
:class:`~sage.rings.noncommutative_ideals.Ideal_nc`.

EXAMPLES::
- Further named arguments (such as ``side`` in the case of
non-commutative rings) are forwarded to the ideal class.

The keyword ``side`` can be one of ``'twosided'``,
``'left'``, ``'right'``. It determines whether
the resulting ideal is twosided, a left ideal or a right ideal.

EXAMPLES:

Matrix rings::

sage: # needs sage.modules
sage: MS = MatrixSpace(QQ, 2, 2)
sage: isinstance(MS, Ring)
False
sage: MS in Rings()
True
sage: MS.ideal(2)
Twosided Ideal
(
Expand All @@ -858,6 +862,36 @@
[0 0]
)
of Full MatrixSpace of 2 by 2 dense matrices over Rational Field

Polynomial rings::

sage: R.<x,y> = QQ[]
sage: R.ideal(x,y)
Ideal (x, y) of Multivariate Polynomial Ring in x, y over Rational Field
sage: R.ideal(x+y^2)
Ideal (y^2 + x) of Multivariate Polynomial Ring in x, y over Rational Field
sage: R.ideal( [x^3,y^3+x^3] )
Ideal (x^3, x^3 + y^3) of Multivariate Polynomial Ring in x, y over Rational Field

Non-commutative rings::

sage: A = SteenrodAlgebra(2) # needs sage.combinat sage.modules
sage: A.ideal(A.1, A.2^2) # needs sage.combinat sage.modules
Twosided Ideal (Sq(2), Sq(2,2)) of mod 2 Steenrod algebra, milnor basis
sage: A.ideal(A.1, A.2^2, side='left') # needs sage.combinat sage.modules
Left Ideal (Sq(2), Sq(2,2)) of mod 2 Steenrod algebra, milnor basis

TESTS:

Make sure that :issue:`11139` is fixed::

sage: R.<x> = QQ[]
sage: R.ideal([])
Principal ideal (0) of Univariate Polynomial Ring in x over Rational Field
sage: R.ideal(())
Principal ideal (0) of Univariate Polynomial Ring in x over Rational Field
sage: R.ideal()
Principal ideal (0) of Univariate Polynomial Ring in x over Rational Field
"""
if 'coerce' in kwds:
coerce = kwds['coerce']
Expand All @@ -866,8 +900,7 @@
coerce = True

from sage.rings.ideal import Ideal_generic
from types import GeneratorType
if len(args) == 0:
if not args:
gens = [self(0)]
else:
gens = args
Expand All @@ -888,27 +921,26 @@
break
elif isinstance(first, (list, tuple, GeneratorType)):
gens = first
elif isinstance(first, Parent) and self.has_coerce_map_from(first):
gens = first.gens() # we have a ring as argument

Check warning on line 925 in src/sage/categories/rings.py

View check run for this annotation

Codecov / codecov/patch

src/sage/categories/rings.py#L925

Added line #L925 was not covered by tests
else:
try:
if self.has_coerce_map_from(first):
gens = first.gens() # we have a ring as argument
elif isinstance(first, Element):
gens = [first]
else:
raise ArithmeticError("there is no coercion from %s to %s" % (first, self))
except TypeError: # first may be a ring element
pass
break
if coerce:

if len(gens) == 0:
gens = [self.zero()]
elif coerce:
gens = [self(g) for g in gens]

from sage.categories.principal_ideal_domains import PrincipalIdealDomains
if self in PrincipalIdealDomains():
# Use GCD algorithm to obtain a principal ideal
g = gens[0]
if len(gens) == 1:
try:
g = g.gcd(g) # note: we set g = gcd(g, g) to "canonicalize" the generator: make polynomials monic, etc.
except (AttributeError, NotImplementedError):
# note: we set g = gcd(g, g) to "canonicalize" the generator:
# make polynomials monic, etc.
g = g.gcd(g)
except (AttributeError, NotImplementedError, IndexError):

Check warning on line 943 in src/sage/categories/rings.py

View check run for this annotation

Codecov / codecov/patch

src/sage/categories/rings.py#L943

Added line #L943 was not covered by tests
pass
else:
for h in gens[1:]:
Expand Down
5 changes: 3 additions & 2 deletions src/sage/rings/number_field/number_field.py
Original file line number Diff line number Diff line change
Expand Up @@ -3534,7 +3534,7 @@ def ideal(self, *gens, **kwds):
try:
return self.fractional_ideal(*gens, **kwds)
except ValueError:
return Ring.ideal(self, gens, **kwds)
return self.zero_ideal()

def idealchinese(self, ideals, residues):
r"""
Expand Down Expand Up @@ -3600,9 +3600,10 @@ def idealchinese(self, ideals, residues):
def fractional_ideal(self, *gens, **kwds):
r"""
Return the ideal in `\mathcal{O}_K` generated by ``gens``.

This overrides the :class:`sage.rings.ring.Field` method to
use the :class:`sage.rings.ring.Ring` one instead, since
we're not really concerned with ideals in a field but in its ring
we are not concerned with ideals in a field but in its ring
of integers.

INPUT:
Expand Down
28 changes: 0 additions & 28 deletions src/sage/rings/polynomial/multi_polynomial_ring.py
Original file line number Diff line number Diff line change
Expand Up @@ -949,31 +949,3 @@ def is_field(self, proof=True):
if self.ngens() == 0:
return self.base_ring().is_field(proof)
return False

def ideal(self, *gens, **kwds):
"""
Create an ideal in this polynomial ring.
"""
do_coerce = False
if len(gens) == 1:
from sage.rings.ideal import Ideal_generic
if isinstance(gens[0], Ideal_generic):
if gens[0].ring() is self:
return gens[0]
gens = gens[0].gens()
elif isinstance(gens[0], (list, tuple)):
gens = gens[0]
if not self._has_singular:
# pass through
MPolynomialRing_base.ideal(self, gens, **kwds)

from sage.rings.polynomial.multi_polynomial_ideal import MPolynomialIdeal

if isinstance(gens, (sage.interfaces.abc.SingularElement, sage.interfaces.abc.Macaulay2Element)):
gens = list(gens)
do_coerce = True
elif not isinstance(gens, (list, tuple)):
gens = [gens]
if ('coerce' in kwds and kwds['coerce']) or do_coerce:
gens = [self(x) for x in gens] # this will even coerce from singular ideals correctly!
return MPolynomialIdeal(self, gens, **kwds)
129 changes: 0 additions & 129 deletions src/sage/rings/ring.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -361,115 +361,6 @@ cdef class Ring(ParentWithGens):
# initialisation has finished.
return self._category or _Rings

def ideal(self, *args, **kwds):
"""
Return the ideal defined by ``x``, i.e., generated by ``x``.

INPUT:

- ``*x`` -- list or tuple of generators (or several input arguments)

- ``coerce`` -- boolean (default: ``True``); this must be a keyword
argument. Only set it to ``False`` if you are certain that each
generator is already in the ring.

- ``ideal_class`` -- callable (default: ``self._ideal_class_()``);
this must be a keyword argument. A constructor for ideals, taking
the ring as the first argument and then the generators.
Usually a subclass of :class:`~sage.rings.ideal.Ideal_generic` or
:class:`~sage.rings.noncommutative_ideals.Ideal_nc`.

- Further named arguments (such as ``side`` in the case of
non-commutative rings) are forwarded to the ideal class.

EXAMPLES::

sage: R.<x,y> = QQ[]
sage: R.ideal(x,y)
Ideal (x, y) of Multivariate Polynomial Ring in x, y over Rational Field
sage: R.ideal(x+y^2)
Ideal (y^2 + x) of Multivariate Polynomial Ring in x, y over Rational Field
sage: R.ideal( [x^3,y^3+x^3] )
Ideal (x^3, x^3 + y^3) of Multivariate Polynomial Ring in x, y over Rational Field

Here is an example over a non-commutative ring::

sage: A = SteenrodAlgebra(2) # needs sage.combinat sage.modules
sage: A.ideal(A.1, A.2^2) # needs sage.combinat sage.modules
Twosided Ideal (Sq(2), Sq(2,2)) of mod 2 Steenrod algebra, milnor basis
sage: A.ideal(A.1, A.2^2, side='left') # needs sage.combinat sage.modules
Left Ideal (Sq(2), Sq(2,2)) of mod 2 Steenrod algebra, milnor basis

TESTS:

Make sure that :issue:`11139` is fixed::

sage: R.<x> = QQ[]
sage: R.ideal([])
Principal ideal (0) of Univariate Polynomial Ring in x over Rational Field
sage: R.ideal(())
Principal ideal (0) of Univariate Polynomial Ring in x over Rational Field
sage: R.ideal()
Principal ideal (0) of Univariate Polynomial Ring in x over Rational Field
"""
if 'coerce' in kwds:
coerce = kwds['coerce']
del kwds['coerce']
else:
coerce = True

from sage.rings.ideal import Ideal_generic
from sage.structure.parent import Parent
gens = args
while isinstance(gens, (list, tuple)) and len(gens) == 1:
first = gens[0]
if isinstance(first, Ideal_generic):
R = first.ring()
m = self.convert_map_from(R)
if m is not None:
gens = [m(g) for g in first.gens()]
coerce = False
else:
m = R.convert_map_from(self)
if m is not None:
raise NotImplementedError
else:
raise TypeError
break
elif isinstance(first, (list, tuple)):
gens = first
elif isinstance(first, Parent) and self.has_coerce_map_from(first):
gens = first.gens() # we have a ring as argument
else:
break

if len(gens) == 0:
gens = [self.zero()]

if coerce:
gens = [self(g) for g in gens]
if self in PrincipalIdealDomains():
# Use GCD algorithm to obtain a principal ideal
g = gens[0]
if len(gens) == 1:
try:
# note: we set g = gcd(g, g) to "canonicalize" the generator: make polynomials monic, etc.
g = g.gcd(g)
except (AttributeError, NotImplementedError, IndexError):
pass
else:
for h in gens[1:]:
g = g.gcd(h)
gens = [g]
if 'ideal_class' in kwds:
C = kwds['ideal_class']
del kwds['ideal_class']
else:
C = self._ideal_class_(len(gens))
if len(gens) == 1 and isinstance(gens[0], (list, tuple)):
gens = gens[0]
return C(self, gens, **kwds)

def __mul__(self, x):
"""
Return the ideal ``x*R`` generated by ``x``, where ``x`` is either an
Expand Down Expand Up @@ -1382,26 +1273,6 @@ cdef class Field(CommutativeRing):
return y.is_zero()
return True

def ideal(self, *gens, **kwds):
"""
Return the ideal generated by gens.

EXAMPLES::

sage: QQ.ideal(2)
Principal ideal (1) of Rational Field
sage: QQ.ideal(0)
Principal ideal (0) of Rational Field
"""
if len(gens) == 1 and isinstance(gens[0], (list, tuple)):
gens = gens[0]
if not isinstance(gens, (list, tuple)):
gens = [gens]
for x in gens:
if not self(x).is_zero():
return self.unit_ideal()
return self.zero_ideal()

def integral_closure(self):
"""
Return this field, since fields are integrally closed in their
Expand Down
Loading