Skip to content

Commit 44d924b

Browse files
author
Release Manager
committed
gh-38821: let the category setup handle the ideals This is removing the `ideal` methods in the old `Ring` and `Field` classes, moving them to the category setup. Also removing one custom `ideal` method in multiple polynomial rings. ### 📝 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. - [ ] I have created tests covering the changes. - [ ] I have updated the documentation and checked the documentation preview. URL: #38821 Reported by: Frédéric Chapoton Reviewer(s): Frédéric Chapoton, Martin Rubey
2 parents 6959ae2 + e423a38 commit 44d924b

File tree

6 files changed

+101
-196
lines changed

6 files changed

+101
-196
lines changed

src/doc/en/thematic_tutorials/coercion_and_categories.rst

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -129,7 +129,6 @@ This base class provides a lot more methods than a general parent::
129129
'fraction_field',
130130
'gen',
131131
'gens',
132-
'ideal',
133132
'integral_closure',
134133
'is_commutative',
135134
'is_field',

src/sage/categories/fields.py

Lines changed: 35 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -193,7 +193,7 @@ def _call_(self, x):
193193

194194
class ParentMethods:
195195

196-
def is_field( self, proof=True ):
196+
def is_field(self, proof=True):
197197
r"""
198198
Return ``True`` as ``self`` is a field.
199199
@@ -458,6 +458,38 @@ def fraction_field(self):
458458
"""
459459
return self
460460

461+
def ideal(self, *gens, **kwds):
462+
"""
463+
Return the ideal generated by ``gens``.
464+
465+
INPUT:
466+
467+
- an element or a list/tuple/sequence of elements, the generators
468+
469+
Any named arguments are ignored.
470+
471+
EXAMPLES::
472+
473+
sage: QQ.ideal(2)
474+
Principal ideal (1) of Rational Field
475+
sage: QQ.ideal(0)
476+
Principal ideal (0) of Rational Field
477+
478+
TESTS::
479+
480+
sage: QQ.ideal(2, 4)
481+
Principal ideal (1) of Rational Field
482+
483+
sage: QQ.ideal([2, 4])
484+
Principal ideal (1) of Rational Field
485+
"""
486+
if len(gens) == 1 and isinstance(gens[0], (list, tuple)):
487+
gens = gens[0]
488+
for x in gens:
489+
if not self(x).is_zero():
490+
return self.unit_ideal()
491+
return self.zero_ideal()
492+
461493
def _squarefree_decomposition_univariate_polynomial(self, f):
462494
r"""
463495
Return the square-free decomposition of ``f`` over this field.
@@ -585,7 +617,7 @@ def quo_rem(self, other):
585617
raise ZeroDivisionError
586618
return (self/other, self.parent().zero())
587619

588-
def is_unit( self ):
620+
def is_unit(self):
589621
r"""
590622
Return ``True`` if ``self`` has a multiplicative inverse.
591623
@@ -602,7 +634,7 @@ def is_unit( self ):
602634
# Of course, in general gcd and lcm in a field are not very interesting.
603635
# However, they should be implemented!
604636
@coerce_binop
605-
def gcd(self,other):
637+
def gcd(self, other):
606638
"""
607639
Greatest common divisor.
608640

src/sage/categories/rings.py

Lines changed: 63 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -12,12 +12,14 @@
1212
# https://www.gnu.org/licenses/
1313
# *****************************************************************************
1414
from functools import reduce
15+
from types import GeneratorType
1516

1617
from sage.misc.cachefunc import cached_method
1718
from sage.misc.lazy_import import LazyImport
1819
from sage.categories.category_with_axiom import CategoryWithAxiom
1920
from sage.categories.rngs import Rngs
2021
from sage.structure.element import Element
22+
from sage.structure.parent import Parent
2123

2224

2325
class Rings(CategoryWithAxiom):
@@ -815,32 +817,34 @@ def ideal(self, *args, **kwds):
815817
"""
816818
Create an ideal of this ring.
817819
818-
.. NOTE::
820+
INPUT:
819821
820-
The code is copied from the base class
821-
:class:`~sage.rings.ring.Ring`. This is
822-
because there are rings that do not inherit
823-
from that class, such as matrix algebras.
824-
See :issue:`7797`.
822+
- an element or a list/tuple/sequence of elements, the generators
825823
826-
INPUT:
824+
- ``coerce`` -- boolean (default: ``True``); whether to first coerce
825+
the elements into this ring. This must be a keyword
826+
argument. Only set it to ``False`` if you are certain that each
827+
generator is already in the ring.
827828
828-
- an element or a list/tuple/sequence of elements
829-
- ``coerce`` -- boolean (default: ``True``);
830-
first coerce the elements into this ring
831-
- ``side`` -- (optional) string, one of ``'twosided'``
832-
(default), ``'left'``, ``'right'``; determines
833-
whether the resulting ideal is twosided, a left
834-
ideal or a right ideal
829+
- ``ideal_class`` -- callable (default: ``self._ideal_class_()``);
830+
this must be a keyword argument. A constructor for ideals, taking
831+
the ring as the first argument and then the generators.
832+
Usually a subclass of :class:`~sage.rings.ideal.Ideal_generic` or
833+
:class:`~sage.rings.noncommutative_ideals.Ideal_nc`.
835834
836-
EXAMPLES::
835+
- Further named arguments (such as ``side`` in the case of
836+
non-commutative rings) are forwarded to the ideal class.
837+
838+
The keyword ``side`` can be one of ``'twosided'``,
839+
``'left'``, ``'right'``. It determines whether
840+
the resulting ideal is twosided, a left ideal or a right ideal.
841+
842+
EXAMPLES:
843+
844+
Matrix rings::
837845
838846
sage: # needs sage.modules
839847
sage: MS = MatrixSpace(QQ, 2, 2)
840-
sage: isinstance(MS, Ring)
841-
False
842-
sage: MS in Rings()
843-
True
844848
sage: MS.ideal(2)
845849
Twosided Ideal
846850
(
@@ -858,6 +862,36 @@ def ideal(self, *args, **kwds):
858862
[0 0]
859863
)
860864
of Full MatrixSpace of 2 by 2 dense matrices over Rational Field
865+
866+
Polynomial rings::
867+
868+
sage: R.<x,y> = QQ[]
869+
sage: R.ideal(x,y)
870+
Ideal (x, y) of Multivariate Polynomial Ring in x, y over Rational Field
871+
sage: R.ideal(x+y^2)
872+
Ideal (y^2 + x) of Multivariate Polynomial Ring in x, y over Rational Field
873+
sage: R.ideal( [x^3,y^3+x^3] )
874+
Ideal (x^3, x^3 + y^3) of Multivariate Polynomial Ring in x, y over Rational Field
875+
876+
Non-commutative rings::
877+
878+
sage: A = SteenrodAlgebra(2) # needs sage.combinat sage.modules
879+
sage: A.ideal(A.1, A.2^2) # needs sage.combinat sage.modules
880+
Twosided Ideal (Sq(2), Sq(2,2)) of mod 2 Steenrod algebra, milnor basis
881+
sage: A.ideal(A.1, A.2^2, side='left') # needs sage.combinat sage.modules
882+
Left Ideal (Sq(2), Sq(2,2)) of mod 2 Steenrod algebra, milnor basis
883+
884+
TESTS:
885+
886+
Make sure that :issue:`11139` is fixed::
887+
888+
sage: R.<x> = QQ[]
889+
sage: R.ideal([])
890+
Principal ideal (0) of Univariate Polynomial Ring in x over Rational Field
891+
sage: R.ideal(())
892+
Principal ideal (0) of Univariate Polynomial Ring in x over Rational Field
893+
sage: R.ideal()
894+
Principal ideal (0) of Univariate Polynomial Ring in x over Rational Field
861895
"""
862896
if 'coerce' in kwds:
863897
coerce = kwds['coerce']
@@ -866,8 +900,7 @@ def ideal(self, *args, **kwds):
866900
coerce = True
867901

868902
from sage.rings.ideal import Ideal_generic
869-
from types import GeneratorType
870-
if len(args) == 0:
903+
if not args:
871904
gens = [self(0)]
872905
else:
873906
gens = args
@@ -889,26 +922,23 @@ def ideal(self, *args, **kwds):
889922
elif isinstance(first, (list, tuple, GeneratorType)):
890923
gens = first
891924
else:
892-
try:
893-
if self.has_coerce_map_from(first):
894-
gens = first.gens() # we have a ring as argument
895-
elif isinstance(first, Element):
896-
gens = [first]
897-
else:
898-
raise ArithmeticError("there is no coercion from %s to %s" % (first, self))
899-
except TypeError: # first may be a ring element
900-
pass
901925
break
902-
if coerce:
926+
927+
if not gens:
928+
gens = [self.zero()]
929+
elif coerce:
903930
gens = [self(g) for g in gens]
931+
904932
from sage.categories.principal_ideal_domains import PrincipalIdealDomains
905933
if self in PrincipalIdealDomains():
906934
# Use GCD algorithm to obtain a principal ideal
907935
g = gens[0]
908936
if len(gens) == 1:
909937
try:
910-
g = g.gcd(g) # note: we set g = gcd(g, g) to "canonicalize" the generator: make polynomials monic, etc.
911-
except (AttributeError, NotImplementedError):
938+
# note: we set g = gcd(g, g) to "canonicalize" the generator:
939+
# make polynomials monic, etc.
940+
g = g.gcd(g)
941+
except (AttributeError, NotImplementedError, IndexError):
912942
pass
913943
else:
914944
for h in gens[1:]:

src/sage/rings/number_field/number_field.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3534,7 +3534,7 @@ def ideal(self, *gens, **kwds):
35343534
try:
35353535
return self.fractional_ideal(*gens, **kwds)
35363536
except ValueError:
3537-
return Ring.ideal(self, gens, **kwds)
3537+
return self.zero_ideal()
35383538

35393539
def idealchinese(self, ideals, residues):
35403540
r"""
@@ -3600,9 +3600,10 @@ def idealchinese(self, ideals, residues):
36003600
def fractional_ideal(self, *gens, **kwds):
36013601
r"""
36023602
Return the ideal in `\mathcal{O}_K` generated by ``gens``.
3603+
36033604
This overrides the :class:`sage.rings.ring.Field` method to
36043605
use the :class:`sage.rings.ring.Ring` one instead, since
3605-
we're not really concerned with ideals in a field but in its ring
3606+
we are not concerned with ideals in a field but in its ring
36063607
of integers.
36073608
36083609
INPUT:

src/sage/rings/polynomial/multi_polynomial_ring.py

Lines changed: 0 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -949,31 +949,3 @@ def is_field(self, proof=True):
949949
if self.ngens() == 0:
950950
return self.base_ring().is_field(proof)
951951
return False
952-
953-
def ideal(self, *gens, **kwds):
954-
"""
955-
Create an ideal in this polynomial ring.
956-
"""
957-
do_coerce = False
958-
if len(gens) == 1:
959-
from sage.rings.ideal import Ideal_generic
960-
if isinstance(gens[0], Ideal_generic):
961-
if gens[0].ring() is self:
962-
return gens[0]
963-
gens = gens[0].gens()
964-
elif isinstance(gens[0], (list, tuple)):
965-
gens = gens[0]
966-
if not self._has_singular:
967-
# pass through
968-
MPolynomialRing_base.ideal(self, gens, **kwds)
969-
970-
from sage.rings.polynomial.multi_polynomial_ideal import MPolynomialIdeal
971-
972-
if isinstance(gens, (sage.interfaces.abc.SingularElement, sage.interfaces.abc.Macaulay2Element)):
973-
gens = list(gens)
974-
do_coerce = True
975-
elif not isinstance(gens, (list, tuple)):
976-
gens = [gens]
977-
if ('coerce' in kwds and kwds['coerce']) or do_coerce:
978-
gens = [self(x) for x in gens] # this will even coerce from singular ideals correctly!
979-
return MPolynomialIdeal(self, gens, **kwds)

0 commit comments

Comments
 (0)