Skip to content
45 changes: 37 additions & 8 deletions src/sage/categories/homset.py
Original file line number Diff line number Diff line change
Expand Up @@ -208,7 +208,7 @@ def Hom(X, Y, category=None, check=True):
with the result for the meet of the categories of the domain and
the codomain::

sage: Hom(QQ, ZZ) is Hom(QQ,ZZ, Category.meet([QQ.category(), ZZ.category()]))
sage: Hom(QQ, ZZ) is Hom(QQ, ZZ, category=QQ.category() | ZZ.category())
True

Some doc tests in :mod:`sage.rings` (need to) break the unique
Expand Down Expand Up @@ -270,9 +270,8 @@ def Hom(X, Y, category=None, check=True):
from <sage.structure.parent.Parent object at ...>
to <sage.structure.parent.Parent object at ...>
sage: Hom(PA, PJ).category()
Category of homsets of
unital magmas and right modules over Rational Field
and left modules over Rational Field
Category of homsets of unital magmas and right modules over Rational
Field and left modules over Rational Field
sage: Hom(PA, PJ, Rngs())
Set of Morphisms
from <sage.structure.parent.Parent object at ...>
Expand Down Expand Up @@ -412,7 +411,7 @@ def Hom(X, Y, category=None, check=True):

# Determines the category
if category is None:
category = X.category()._meet_(Y.category())
category = X.category() | Y.category()
# Recurse to make sure that Hom(X, Y) and Hom(X, Y, category) are identical
# No need to check the input again
H = Hom(X, Y, category, check=False)
Expand Down Expand Up @@ -654,11 +653,23 @@ def __init__(self, X, Y, category=None, base=None, check=True):

sage: MyHomset(ZZ^3, ZZ^3, base=QQ).base_ring() # needs sage.modules
Rational Field

Check that :issue:`37730` is resolved::

sage: E = EllipticCurve(QQ, [3, 5])
sage: E.point_homset().category()
Join of Category of commutative additive groups and Category of
homsets of schemes over Rational Field
sage: E.point_homset() in CommutativeAdditiveGroups()
True
sage: from sage.schemes.generic.homset import SchemeHomset_points
sage: SchemeHomset_points(Spec(QQ), E) in CommutativeAdditiveGroups()
True
"""
self._domain = X
self._codomain = Y
if category is None:
category = X.category()
category = X.category() | Y.category()
self.__category = category
if check:
if not isinstance(category, Category):
Expand All @@ -676,8 +687,26 @@ def __init__(self, X, Y, category=None, base=None, check=True):
# See also #15801.
base = X.base_ring()

Parent.__init__(self, base=base,
category=category.Endsets() if X is Y else category.Homsets())
# Hom(X, AbGrp) has an AbGrp structure by pointwise operation. Not sure
# if we should also inherit the multiplication operation when it exists
category = category.Endsets() if X is Y else category.Homsets()

# We handle abelian varieties as a special case, since they themselves
# don't have an abelian group, only their point homsets, see discussion
# under #37768.
from sage.categories.schemes import Sets, AbelianVarieties
from sage.categories.commutative_additive_groups import CommutativeAdditiveGroups

# TODO: Uncomment this once sum of morphisms is implemented (see #37768)
# group_cat = Y.category() | CommutativeAdditiveGroups()
group_cat = Sets()
try:
if Y in AbelianVarieties(Y._base_scheme):
group_cat = CommutativeAdditiveGroups()
except (AttributeError, ValueError):
pass

Parent.__init__(self, base=base, category=category & group_cat)

def __reduce__(self):
"""
Expand Down
75 changes: 59 additions & 16 deletions src/sage/categories/schemes.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,12 +23,13 @@
from sage.categories.rings import Rings
from sage.categories.fields import Fields
from sage.categories.homsets import HomsetsCategory
from sage.structure.unique_representation import UniqueRepresentation
from sage.misc.abstract_method import abstract_method
from sage.misc.lazy_import import lazy_import

lazy_import('sage.categories.map', 'Map')
lazy_import('sage.schemes.generic.morphism', 'SchemeMorphism')
lazy_import('sage.schemes.generic.scheme', 'Scheme')
lazy_import('sage.schemes.generic.scheme', ['Scheme', 'AffineScheme'])


class Schemes(Category):
Expand Down Expand Up @@ -197,7 +198,6 @@ def _repr_object_names(self):
sage: Schemes(Spec(ZZ)) # indirect doctest
Category of schemes over Integer Ring
"""
from sage.schemes.generic.scheme import AffineScheme
base = self.base()
if isinstance(base, AffineScheme):
base = base.coordinate_ring()
Expand All @@ -206,7 +206,7 @@ def _repr_object_names(self):

class AbelianVarieties(Schemes_over_base):
r"""
The category of abelian varieties over a given field.
The category of abelian varieties over a given field scheme.

EXAMPLES::

Expand All @@ -217,22 +217,43 @@ class AbelianVarieties(Schemes_over_base):
...
ValueError: category of abelian varieties is only defined over fields
"""
@staticmethod
def __classcall__(cls, base):
r"""
Normalise arguments for the constructor of the
:class:`AbelianVarieties` category.

EXAMPLES::

sage: AbelianVarieties(QQ) is AbelianVarieties(Spec(QQ)) # indirect doctest
True
"""
if not isinstance(base, Scheme):
base = Schemes()(base)
if not (isinstance(base, AffineScheme) and base.coordinate_ring() in Fields()):
raise ValueError('category of abelian varieties is only defined over fields')
return UniqueRepresentation.__classcall__(cls, base)

def __init__(self, base):
r"""
Constructor for the ``AbelianVarieties`` category.
Cosntructor for the :class:`AbelianVarieties` category.

EXAMPLES::

sage: AbelianVarieties(QQ)
sage: A = AbelianVarieties(QQ); A
Category of abelian varieties over Rational Field
sage: AbelianVarieties(Spec(QQ))
sage: B = AbelianVarieties(Spec(QQ)); B
Category of abelian varieties over Rational Field
sage: A is B
True

The category of abelian varieties is only defined over fields::

sage: AbelianVarieties(ZZ)
Traceback (most recent call last):
...
ValueError: category of abelian varieties is only defined over fields
"""
from sage.schemes.generic.scheme import AffineScheme
if isinstance(base, AffineScheme):
base = base.coordinate_ring()
if base not in Fields():
raise ValueError('category of abelian varieties is only defined over fields')
super().__init__(base)

def base_scheme(self):
Expand All @@ -252,10 +273,9 @@ def super_categories(self):
EXAMPLES::

sage: AbelianVarieties(QQ).super_categories()
[Category of schemes over Rational Field,
Category of commutative additive groups]
[Category of schemes over Rational Field]
"""
return [Schemes(self.base_scheme()), CommutativeAdditiveGroups()]
return [Schemes(self.base_scheme())]

def _repr_object_names(self):
"""
Expand All @@ -264,7 +284,31 @@ def _repr_object_names(self):
sage: AbelianVarieties(Spec(QQ)) # indirect doctest
Category of abelian varieties over Rational Field
"""
return "abelian varieties over %s" % self.base()
if isinstance(self.base_scheme(), AffineScheme):
return "abelian varieties over %s" % self.base_scheme().coordinate_ring()
else:
return "abelian varieties over %s" % self.base_scheme()

class ParentMethods:
def zero(self):
"""
Return the additive identity of this abelian variety.

EXAMPLES::

sage: E = EllipticCurve([1, 3])
sage: E.zero()
(0 : 1 : 0)
sage: E.zero() == E(0)
True
"""
try:
return self(0)
except Exception:
try:
return self.point_homset()(0)
except Exception:
raise NotImplementedError

class Homsets(HomsetsCategory):
r"""
Expand Down Expand Up @@ -331,7 +375,6 @@ def __init__(self, base):
sage: Jacobians(Spec(QQ))
Category of Jacobians over Rational Field
"""
from sage.schemes.generic.scheme import AffineScheme
if isinstance(base, AffineScheme):
base = base.coordinate_ring()
if base not in Fields():
Expand Down
7 changes: 3 additions & 4 deletions src/sage/modules/free_module.py
Original file line number Diff line number Diff line change
Expand Up @@ -4416,10 +4416,9 @@ def _Hom_(self, Y, category):
sage: type(H)
<class 'sage.modules.free_module_homspace.FreeModuleHomspace_with_category'>
sage: H
Set of Morphisms from Vector space of dimension 2 over Rational Field
to Ambient free module of rank 3 over the principal ideal domain Integer Ring
in Category of finite dimensional vector spaces with basis over
(number fields and quotient fields and metric spaces)
Set of Morphisms from Vector space of dimension 2 over Rational
Field to Ambient free module of rank 3 over the principal ideal
domain Integer Ring in Category of commutative additive groups
"""
if Y.base_ring().is_field():
from sage.modules import vector_space_homspace
Expand Down
43 changes: 30 additions & 13 deletions src/sage/schemes/elliptic_curves/ell_generic.py
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,12 @@
sage: P = E([-1,1,1])
sage: -5*P
(179051/80089 : -91814227/22665187 : 1)

::

sage: for R in [Zmod(10), QQ, NumberField(x^3 + 5, "a"), Qp(3), GF(2), GF(3), GF((7, 3))]:
....: params = [3, 5] if R.characteristic() not in [2, 3] else [1, 0, 1, 3, 5]
....: TestSuite(EllipticCurve(R, params)).run(skip="_test_not_implemented_methods")
"""
def __init__(self, K, ainvs, category=None):
r"""
Expand Down Expand Up @@ -601,8 +607,8 @@
[(0 : 1 : 0)]
"""
if len(args) == 1 and args[0] == 0:
R = self.base_ring()
return self.point([R(0),R(1),R(0)], check=False)
R = self.__base_ring
return self.point([R.zero(), R.one(), R.zero()], check=False)
P = args[0]
if isinstance(P, groups.AdditiveAbelianGroupElement) and isinstance(P.parent(),ell_torsion.EllipticCurveTorsionSubgroup):
return self(P.element())
Expand All @@ -620,6 +626,20 @@

return plane_curve.ProjectivePlaneCurve.__call__(self, *args, **kwds)

def zero(self):
"""
Return the additive identity of this abelian variety.

EXAMPLES::

sage: E = EllipticCurve(Zmod(10), [1, 3])
sage: E.zero()
(0 : 1 : 0)
sage: E.zero() == E(0)
True
"""
return self(0)

def _reduce_point(self, R, p):
r"""
Reduces a point R on an elliptic curve to the corresponding point on
Expand Down Expand Up @@ -3667,8 +3687,8 @@
<... 'cypari2.gen.Gen'>
sage: e.type()
't_VEC'
sage: e.disc()

Check failure on line 3690 in src/sage/schemes/elliptic_curves/ell_generic.py

View workflow job for this annotation

GitHub Actions / test-new

Failed example:

Failed example:: Got: 37.0000000000000
37.0000000000000
37.00000000000000000

Over a finite field::

Expand All @@ -3694,17 +3714,14 @@

sage: K.<a> = QuadraticField(2) # needs sage.libs.pari sage.rings.number_field
sage: E = EllipticCurve([1,a]) # needs sage.libs.pari sage.rings.number_field
sage: E.pari_curve() # needs sage.libs.pari sage.rings.number_field

Check failure on line 3717 in src/sage/schemes/elliptic_curves/ell_generic.py

View workflow job for this annotation

GitHub Actions / test-new

Failed example:

Failed example:: Got: [0, 0, 0, Mod(1, y^2 - 2), Mod(y, y^2 - 2), 0, Mod(2, y^2 - 2), Mod(4*y, y^2 - 2), Mod(-1, y^2 - 2), Mod(-48, y^2 - 2), Mod(-864*y, y^2 - 2), Mod(-928, y^2 - 2), Mod(3456/29, y^2 - 2), Vecsmall([5]), [[y^2 - 2, [2, 0], 8, 1, [[1, -1.41421356237310; 1, 1.41421356237310], [1, -1.41421356237310; 1, 1.41421356237310], [16, -23; 16, 23], [2, 0; 0, 4], [4, 0; 0, 2], [2, 0; 0, 1], [2, [0, 2; 1, 0]], [2]], [-1.41421356237310, 1.41421356237310], [1, y], [1, 0; 0, 1], [1, 0, 0, 2; 0, 1, 1, 0]]], [0, 0, 0, 0, 0]]
[0, 0, 0, Mod(1, y^2 - 2),
Mod(y, y^2 - 2), 0, Mod(2, y^2 - 2), Mod(4*y, y^2 - 2),
Mod(-1, y^2 - 2), Mod(-48, y^2 - 2), Mod(-864*y, y^2 - 2),
Mod(-928, y^2 - 2), Mod(3456/29, y^2 - 2),
Vecsmall([5]),
[[y^2 - 2, [2, 0], 8, 1, [[1, -1.41421356237310; 1, 1.41421356237310],
[1, -1.41421356237310; 1, 1.41421356237310],
[16, -23; 16, 23], [2, 0; 0, 4], [4, 0; 0, 2], [2, 0; 0, 1],
[2, [0, 2; 1, 0]], [2]], [-1.41421356237310, 1.41421356237310],
[1, y], [1, 0; 0, 1], [1, 0, 0, 2; 0, 1, 1, 0]]], [0, 0, 0, 0, 0]]
[0, 0, 0, Mod(1, y^2 - 2), Mod(y, y^2 - 2), 0, Mod(2, y^2 - 2), Mod(4*y, y^2 - 2),
Mod(-1, y^2 - 2), Mod(-48, y^2 - 2), Mod(-864*y, y^2 - 2), Mod(-928, y^2 - 2),
Mod(3456/29, y^2 - 2), Vecsmall([5]), [[y^2 - 2, [2, 0], 8, 1, [[1,
-1.414213562373095049; 1, 1.414213562373095049], [1, -1.414213562373095049; 1,
1.414213562373095049], [16, -23; 16, 23], [2, 0; 0, 4], [4, 0; 0, 2], [2, 0; 0, 1], [2,
[0, 2; 1, 0]], [2]], [-1.414213562373095049, 1.414213562373095049], [1, y], [1, 0; 0,
1], [1, 0, 0, 2; 0, 1, 1, 0]]], [0, 0, 0, 0, 0]]

PARI no longer requires that the `j`-invariant has negative `p`-adic valuation::

Expand Down
16 changes: 14 additions & 2 deletions src/sage/schemes/generic/homset.py
Original file line number Diff line number Diff line change
Expand Up @@ -436,18 +436,30 @@ def __init__(self, X, Y, category=None, check=True, base=ZZ):
"""
Python constructor.

This class should not be constructed directly. Instead, use the
:func:`sage.schemes.generic.homset.SchemeHomset` function or the
``point_homset`` method, which initializes the correct category etc.

INPUT:

See :class:`SchemeHomset_generic`.

EXAMPLES::

sage: from sage.schemes.generic.homset import SchemeHomset_points
sage: SchemeHomset_points(Spec(QQ), AffineSpace(ZZ,2))
sage: SchemeHomset_points(Spec(QQ), AffineSpace(ZZ, 2))
Set of rational points of Affine Space of dimension 2 over Rational Field

This constructor might not initialize the correct category::

sage: E = EllipticCurve(QQ, [3, 6])
sage: E.point_homset(QQ)
Abelian group of points on Elliptic Curve defined by y^2 = x^3 + 3*x + 6 over Rational Field
sage: SchemeHomset_points(Spec(QQ), E)
Set of rational points of Elliptic Curve defined by y^2 = x^3 + 3*x + 6 over Rational Field
"""
if check and not isinstance(X, AffineScheme):
raise ValueError('The domain must be an affine scheme.')
raise ValueError('The domain must be an affine scheme')
SchemeHomset_generic.__init__(self, X, Y, category=category, check=check, base=base)

def __reduce__(self):
Expand Down
12 changes: 12 additions & 0 deletions src/sage/schemes/generic/scheme.py
Original file line number Diff line number Diff line change
Expand Up @@ -290,6 +290,17 @@ def point_homset(self, S=None):
Set of rational points of Projective Space of dimension 3 over
Finite Field of size 11

The set of S-valued points of an abelian variety has an abelian group structure::

sage: E = EllipticCurve(QQ, [3, 6])
sage: E in AbelianVarieties(QQ)
True
sage: E.point_homset() in CommutativeAdditiveGroups()
True
sage: K.<a> = NumberField(x^3 + 2)
sage: E.point_homset(K) in CommutativeAdditiveGroups()
True

TESTS::

sage: P = ProjectiveSpace(QQ, 3)
Expand All @@ -302,6 +313,7 @@ def point_homset(self, S=None):
if S is None:
S = self.base_ring()
SpecS = AffineScheme(S, self.base_ring())

from sage.schemes.generic.homset import SchemeHomset
return SchemeHomset(SpecS, self, as_point_homset=True)

Expand Down
4 changes: 3 additions & 1 deletion src/sage/schemes/hyperelliptic_curves/jacobian_generic.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@ class HyperellipticJacobian_generic(Jacobian_generic):
sage: f = x**5 + 1184*x**3 + 1846*x**2 + 956*x + 560
sage: C = HyperellipticCurve(f)
sage: J = C.jacobian()
sage: J in AbelianVarieties(FF)
True
sage: a = x**2 + 376*x + 245; b = 1015*x + 1368
sage: X = J(FF)
sage: D = X([a,b])
Expand Down Expand Up @@ -163,7 +165,7 @@ def dimension(self):

def point(self, mumford, check=True):
try:
return self(self.base_ring())(mumford)
return self.point_homset()(mumford)
except AttributeError:
raise ValueError("Arguments must determine a valid Mumford divisor.")

Expand Down
Loading
Loading