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

Implementing SchemeMorphism_sum #37705

Draft
wants to merge 21 commits into
base: develop
Choose a base branch
from

Conversation

grhkm21
Copy link
Contributor

@grhkm21 grhkm21 commented Mar 31, 2024

This PR implements a SchemeMorphism_sum class, which is a sum of scheme morphisms. It is refactored out from @yyyyx4's EllipticHom_sum class for sum of elliptic curve homomorphisms (zero or isogeny). As a result, we can now mess around with sum of jacobian homomorphisms. This is a step towards defining the endomorphism ring of um things (elliptic curves and jacobians?).

cc: @kwankyu

Four questions:

TODO:

  • Move/copy tests from elliptic curves to morphism.py
  • Use FormalSum or similar to store the summands no, as not all morphisms are hashable.
  • Figure out what's happening with SchemeMorphism vs Morphism - inheriting elliptic curve morphisms from SchemeMorphism fails.

return s

def _add_(self, other):
r"""
Copy link
Contributor

Choose a reason for hiding this comment

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

I think this _add_ method should only be provided when the underlying scheme is in the category of additive magmas.

sage: E = EllipticCurve(j=42)
sage: E.categories()
[Category of abelian varieties over Rational Field,
 Category of commutative additive groups,
 Category of additive groups,
 Category of additive inverse additive unital additive magmas,
 Category of commutative additive monoids,
 Category of additive monoids,
 Category of additive unital additive magmas,
 Category of commutative additive semigroups,
 Category of additive commutative additive magmas,
 Category of additive semigroups,
 Category of additive magmas,
 Category of schemes over Rational Field,
 Category of schemes,
 Category of sets,
 Category of sets with partial maps,
 Category of objects]

Copy link
Contributor

Choose a reason for hiding this comment

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

The problem: For Jacobians of HyperellipticCurve, nothing is declared. What laws does the addition of points of it satisfy?

sage: R.<x> = QQ[]
            sage: J = HyperellipticCurve(x^5 + x + 1).jacobian()
            sage: phi = End(J).identity()
sage: EJ = End(J)
sage: EJ.categories()
[Category of endsets of schemes over Rational Field,
 Category of endsets,
 Category of monoids,
 Category of semigroups,
 Category of unital magmas,
 Category of magmas,
 Category of homsets of schemes over Rational Field,
 Category of homsets,
 Category of sets,
 Category of sets with partial maps,
 Category of objects]

Copy link
Contributor Author

Choose a reason for hiding this comment

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

It has an abelian group structure - it's basically a $\mathbb{Z}$-module (with some group quotients).

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I will look into how to add this categories structure, I'm not too familiar with it.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I have fixed the jacobian issue so that (nice) jacobians have an abelian variety structure.

sage: EllipticCurve(j=42).categories()
[Category of abelian varieties over Rational Field,
 Category of commutative additive groups,
 Category of additive groups,
 Category of additive inverse additive unital additive magmas,
 Category of commutative additive monoids,
 Category of additive monoids,
 Category of additive unital additive magmas,
 Category of commutative additive semigroups,
 Category of additive commutative additive magmas,
 Category of additive semigroups,
 Category of additive magmas,
 Category of schemes over Rational Field,
 Category of schemes,
 Category of sets,
 Category of sets with partial maps,
 Category of objects]
sage: Jacobian_generic(HyperellipticCurve(x^5 + 1)).categories() == EllipticCurve(j=42).categories()
True

You mentioned that

I think this _add_ method should only be provided when the underlying scheme is in the category of additive magmas.

How should I implement this? Should I check if self in AdditiveMagmas() and other in AdditiveMagmas()?

Copy link
Contributor

Choose a reason for hiding this comment

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

I think you can just provide it via the category, as AbelianVarieties.MorphismMethods._add_ etc.

@mkoeppe
Copy link
Contributor

mkoeppe commented Mar 31, 2024

I think you'll be better off with making a class for arbitrary finite ZZ-linear combinations of the morphisms, not just sums. Then you can implement not just _add_ but also _lmul_, _rmul_ with integers, etc.

@grhkm21
Copy link
Contributor Author

grhkm21 commented Mar 31, 2024

I think you'll be better off with making a class for arbitrary finite ZZ-linear combinations of the morphisms, not just sums. Then you can implement not just _add_ but also _lmul_, _rmul_ with integers, etc.

Okay, that should just be FormalSums then?

@mkoeppe
Copy link
Contributor

mkoeppe commented Mar 31, 2024

sort of, but the implementation of FormalSums is not in very good shape

@mkoeppe
Copy link
Contributor

mkoeppe commented Mar 31, 2024

What FormalSums is lacking: control over what elements are allowed.

@grhkm21
Copy link
Contributor Author

grhkm21 commented Mar 31, 2024

I can overload the constructor of SchemeMorphism_sum and check the correctness of types there, I guess. Let me try it first.

Just a heads up, this PR is my first time writing any structural code, and I had 0 experience with Parent/Element/Category's. I'm just reading the wiki as I go..

@grhkm21
Copy link
Contributor Author

grhkm21 commented Apr 1, 2024

I don't understand how multi-hierarchy should be done in Sage. Here I want the Homset_sum (let's say X) to be both a FormalSum and a SchemeMorphism. If I put FormalSum first, then X.parent() would be FormalSums(), which is undesirable. If I put SchemeMorphism first, then X + X will call the _add_ method of SchemeMorphism (as a fallback) instead of FormalSum, which is undesirable.

I think I won't inherit from FormalSum, but instead modify the previous implementation so that instead of self._phis being a list of summands, it is a defaultdict(int). I have an implementation that I can push soon

@mkoeppe
Copy link
Contributor

mkoeppe commented Apr 1, 2024

Yes, don't try to inherit from FormalSum.
(If you really want to reuse any functionality from FormalSum, just store an instance of it and delegate to it, i.e., use the "has-a", not the "is-a" pattern.)

@grhkm21
Copy link
Contributor Author

grhkm21 commented Apr 1, 2024

Update: I don't think this is possible, since not everything is hashable. For example, consider hashing an endomorphism of an elliptic curve over $\mathbb{Q}_3$, where you map $x$ to $ax$, $a \in \mathbb{Q}_3$ or whatever. You eventually have to hash $a$, which is not okay due to #11895.

@grhkm21
Copy link
Contributor Author

grhkm21 commented Apr 2, 2024

Okay I spent an unnecessary large amount of time on this and here are some thoughts for future people who come across this

  1. EllipticCurveHom cannot inherit from SchemeMorphism (for now)

Currently, EllipticCurveHom inherits from Morphism, which is completely independent from SchemeMorphism:

sage: SchemeMorphism.mro()
[<class 'sage.schemes.generic.morphism.SchemeMorphism'>,
 <class 'sage.structure.element.Element'>,
 <class 'sage.structure.sage_object.SageObject'>,
 <class 'object'>]
sage: EllipticCurveHom.mro()
[<class 'sage.schemes.elliptic_curves.hom.EllipticCurveHom'>,
 <class 'sage.categories.morphism.Morphism'>,
 <class 'sage.categories.map.Map'>,
 <class 'sage.structure.element.Element'>,
 <class 'sage.structure.sage_object.SageObject'>,
 <class 'object'>]

A natural solution would be to try to make SchemeMorphism inherit from Morphism. However, you run into approximately a billion issues. Consider an elliptic curve point:

sage: E = EllipticCurve(GF(101), [5, 5])
....: P = E.random_point()
....: type(P).mro()
[...
 <class 'sage.structure.element.ModuleElement'>,
 ...
 <class 'sage.schemes.generic.morphism.SchemeMorphism'>,
 ...]

It is both a ModuleElement and SchemeMorphism, and if the fix above is implemented then it will also be a Morphism and hence a Map. Suddenly, multiplication fails now, because there is a special path for multiplication of ModuleElement in the coercion model (it's treated as a Module action), whereas without it the coercion model falls back to binary addition. One fix would be to add some empty _call_ and _call_by_args_ methods, but this is undesirable in general since SchemeMorphisms are still morphisms that can be called. Another fix would be to change the coercion model code, but no one on sage-devel replied to my message.

  1. Categories

Matthias suggested providing the _add_ method of SchemeMorphisms at the level of category. However, I'm not familiar enough yet, so I have changed it from PR_TODO (i.e. TODO that should be fixed in this PR) to TODO.

  1. TODO: Use more categories in schemes (and fix them)

It seems to me that categories is a really useful concept that should be used more in Sage, especially since it can serve as a "typeclass" system (or traits in Rust) independent from Python's types. I think they should be used more, e.g. instead of testing x = P.an_element(); x + (-x) doesn't error, we should just test P in AdditiveGroups() (I think).

@grhkm21
Copy link
Contributor Author

grhkm21 commented Apr 2, 2024

Something about "Test modularized distributions" is failing, and not just for me

@nbruin
Copy link
Contributor

nbruin commented Apr 2, 2024

Concerning the motivation in the ticket: it would look to me you'd first need endomorphism rings of abelian varieties and then homomorphisms to/from them can be modules over them (by pre- and/or post-composition).

Concerning changes to the coercion framework: I guess the main issue with morphisms sets that also form Z-modules is that there's the very general (partial) composition operation and that there is a distinguished set of endomorphisms somewhere that is canonically isomorphic to Z, which produces an action of Z on the set as well. It may well be possible to tweak things so that this works. It works for matrices ... However, whenever you tweak the coercion system you have to be very careful about performance: it tries quite hard to stay out of the way of low-level operations (or at least provide a fast path for it) and that's easily ruined by small changes.

The other thing is a general design: it's of course nice if an object can be everything it is at once, but we have forgetful functors for a reason in maths: if you overload all meaning onto an object you could end up with a very restricted notion of morphism. You'll have to be able to dress down the information you're keeping track of so that you can also consider the object in a category with more permissive morphisms. So I think in many cases it will be more flexible to just implement the particular view you're interested in and facilitate easy conversion to objects that emphasize other aspects, and I suspect in some cases you may have to go with that approach. It's not clear to me whether this is one of those cases.

@mkoeppe
Copy link
Contributor

mkoeppe commented Apr 2, 2024

A relevant old ticket:

@mkoeppe
Copy link
Contributor

mkoeppe commented Apr 2, 2024

Something about "Test modularized distributions" is failing

Yes, unrelated to this PR and on my list of things to fix.

To: Closed subscheme of Affine Space of dimension 2 over Rational Field
defined by: x*y - 1
Set of scheme endomorphisms of Closed subscheme of Affine Space of dimension 2 over Rational Field defined by:
x*y - 1
Copy link
Contributor

Choose a reason for hiding this comment

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

@grhkm21
Copy link
Contributor Author

grhkm21 commented Apr 2, 2024

@nbruin

Concerning the motivation in the ticket: it would look to me you'd first need endomorphism rings of abelian varieties and then homomorphisms to/from them can be modules over them (by pre- and/or post-composition).

Do you mind explaining this a bit more? What should the classes inheritance look like? There is End(A) == Hom(A, A) already, and I can make it a ring for abelian varieties, if that's what you mean.

Thanks for the other two comments, I will think about these. I agree that sometimes it's more appropriate to separate functionalities, it will just be the most convenient if both functionalities exist at the same time, but maybe in this case that'll make things a lot messier.

@grhkm21
Copy link
Contributor Author

grhkm21 commented Apr 2, 2024

A relevant old ticket:

* [Coercion for homsets #14279](https://github.com/sagemath/sage/issues/14279)

I will have a look at that. This line seems interesting:

One important change: I let sage.categories.map.Map inherit from sage.structure.element.ModuleElement. Not all homsets are modules, of course, but when they are, then one can simply define single underscore _add_, _rmul_ and _composition_ (the latter is for composition of maps) as usual, so that one does not need to overload __mul__ so often.

They basically do a mathematically wrong thing, but say "when it's mathematically wrong, just don't define the relevant methods"...

@grhkm21
Copy link
Contributor Author

grhkm21 commented Apr 2, 2024

Very nice to see that all examples in #14279 have been sped up by ~5x though, either by my CPU or by Sage

@@ -582,7 +644,7 @@ def _coerce_map_from_(self, other):
except AttributeError: # no .ambient_space
return False
elif isinstance(other, SchemeHomset_points):
#we are converting between scheme points
#we are converting between scheme points
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
#we are converting between scheme points
# we are converting between scheme points

Comment on lines 729 to 734
Sum morphism:
From: Jacobian of Hyperelliptic Curve over Rational Field defined by y^2 = x^5 - 8*x
To: Jacobian of Hyperelliptic Curve over Rational Field defined by y^2 = x^5 - 8*x
Via: (Scheme endomorphism of Jacobian of Hyperelliptic Curve over Rational Field defined by y^2 = x^5 - 8*x
Defn: Identity map, Scheme endomorphism of Jacobian of Hyperelliptic Curve over Rational Field defined by y^2 = x^5 - 8*x
Defn: Identity map)
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
Sum morphism:
From: Jacobian of Hyperelliptic Curve over Rational Field defined by y^2 = x^5 - 8*x
To: Jacobian of Hyperelliptic Curve over Rational Field defined by y^2 = x^5 - 8*x
Via: (Scheme endomorphism of Jacobian of Hyperelliptic Curve over Rational Field defined by y^2 = x^5 - 8*x
Defn: Identity map, Scheme endomorphism of Jacobian of Hyperelliptic Curve over Rational Field defined by y^2 = x^5 - 8*x
Defn: Identity map)
Sum morphism:
From: Jacobian of Hyperelliptic Curve over Rational Field defined by y^2 = x^5 - 8*x
To: Jacobian of Hyperelliptic Curve over Rational Field defined by y^2 = x^5 - 8*x
Via: (Scheme endomorphism of Jacobian of Hyperelliptic Curve over Rational Field defined by y^2 = x^5 - 8*x
Defn: Identity map,
Scheme endomorphism of Jacobian of Hyperelliptic Curve over Rational Field defined by y^2 = x^5 - 8*x
Defn: Identity map)

Copy link

Documentation preview for this PR (built with commit 95e0930; changes) is ready! 🎉

@grhkm21
Copy link
Contributor Author

grhkm21 commented May 3, 2024

I will fix this after my exams are done, but I think a more productive and better way to approach this is to fix the issue with SchemeMorphism and Morphism being incompatible first, just so we don't duplicate code between the two classes... Otherwise, we will need both Morphism_sum and SchemeMorphism_sum

@grhkm21
Copy link
Contributor Author

grhkm21 commented May 23, 2024

draft until I can think of a sane way to implement this that does not copy and pasting more code and maintaining the same code on two different places

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants