sagemath / sage

Main repository of SageMath
https://www.sagemath.org
Other
1.47k stars 486 forks source link

Finitely presented modules over the Steenrod algebra #30680

Closed catanzaromj closed 2 years ago

catanzaromj commented 4 years ago

This package implements finitely presented modules over the mod p Steenrod algebra. We define classes for such finitely presented modules, their elements, and morphisms between them. Methods are provided for doing some homological algebra, e.g., computing kernels and images of morphisms, and finding free resolutions of modules.

Depends on #32505 Depends on #33275 Depends on #33323

CC: @sverre320 @sagetrac-kvanwoerden @jhpalmieri @tscrim @rrbruner @cnassau

Component: algebra

Keywords: Steenrod algebra, modules, homological algebra

Author: Bob Bruner, Michael Catanzaro, Sverre Lunøe-Nielsen, Koen van Woerden

Branch/Commit: 7035ae5

Reviewer: John Palmieri, Travis Scrimshaw

Issue created by migration from https://trac.sagemath.org/ticket/30680

jhpalmieri commented 2 years ago
comment:79

Maybe you're proposing that we don't have to do an explicit conversion of the maps if we handle free modules over the Steenrod algebra differently.

tscrim commented 2 years ago
comment:80

I think the code is trying telling us something here. With everything being a free module (and we don't have a special class for free modules over the Steenrod algebra), we should not need to change anything with the resolution. Likely there is an incompatibility between the classes. Perhaps we just need a change_ring() in the free module code that calls the new ring's free_graded_module() method? Any subsequent failures likely means that we need to add methods to the free module and/or subclass it for the Steenrod algebra as per your comment:77. We might also want to consider having different tests for freeness via the has_relations() method, which now that I think of it should be refactored to an is_free() method (or perhaps the latter method just added).

Actually, in comment:76, we already basically have 1. with the free_graded_module() method to the base_ring(). We might want to actually use that instead in resolution().

7ed8c4ca-6d56-4ae9-953a-41e42b4ed313 commented 2 years ago

Changed commit from a2dc039 to 26536af

7ed8c4ca-6d56-4ae9-953a-41e42b4ed313 commented 2 years ago

Branch pushed to git repo; I updated commit sha1. New commits:

29dbd23Last tweaks using super().
b7dfadaMerge branch 't/33275/fp-module-bugs' into t/30680/finitely_presented_modules_over_the_steenrod_algebra
26536aftrac 30680: correct bug from #33275
jhpalmieri commented 2 years ago
comment:82

I have an alternate proposal for some of this: change various _repr_ methods. I don't see why free module morphisms need to be written as "Free module morphism from Free module ...", so I propose changing it to "Module morphism from Free module ...". The information about the types of modules is still there in the domain and codomain.

Also, for modules which are defined as FPModule but with no relations, why not print those as "Free graded left module on 3 generators" rather than "Finitely presented left module on 3 generators and 0 relations"? Whether they are an instance of FPModule or FreeGradedModule, the print representation could be the same.

tscrim commented 2 years ago
comment:83

Both of those are fine with me, but it could make it harder to detect incompatibilities between the objects as we would need to look at the type rather than the printing output. Although I don't think the particular repr output for the resolution is so important though since it is just updating some doctests.

7ed8c4ca-6d56-4ae9-953a-41e42b4ed313 commented 2 years ago

Changed commit from 26536af to 299b7b3

7ed8c4ca-6d56-4ae9-953a-41e42b4ed313 commented 2 years ago

Branch pushed to git repo; I updated commit sha1. New commits:

299b7b3trac 30680: change free module morphism `_repr_` to use "Module morphism"
7ed8c4ca-6d56-4ae9-953a-41e42b4ed313 commented 2 years ago

Changed commit from 299b7b3 to f7cec1d

7ed8c4ca-6d56-4ae9-953a-41e42b4ed313 commented 2 years ago

Branch pushed to git repo; I updated commit sha1. New commits:

f7cec1dtrac 30680: change `_repr_` for a finitely presented module
7ed8c4ca-6d56-4ae9-953a-41e42b4ed313 commented 2 years ago

Changed commit from f7cec1d to 36404aa

7ed8c4ca-6d56-4ae9-953a-41e42b4ed313 commented 2 years ago

Branch pushed to git repo; I updated commit sha1. New commits:

36404aatrac 30680: convert maps in resolution of SteenrodFPModule from maps
jhpalmieri commented 2 years ago
comment:87

Here's a patch that implements the conversion. In order to make the conversion as nice as possible, including keeping track of the names of the generators, I added an argument names to the main Module class (in sage/modules/module.pyx): that class already calls Parent.__init__(...) and Parent can take a names argument, so this seemed like the best way to manage names for module generators. This was already done in the free module case because CombinatorialFreeModule handles names right, but in the non-free case we are just using Module.__init__(...).

Everything in sage/modules passes tests for me. I'll run full tests, too, but meanwhile I'm marking this as "needs review".

jhpalmieri commented 2 years ago
comment:88

By the way, it would be nice to add some odd primary examples. I will try to work on that, but I'm happy for suggestions/contributions, too.

jhpalmieri commented 2 years ago
comment:89

The first odd primary example I tried didn't work. First I found a few bugs:

diff --git a/src/sage/modules/fp_graded/steenrod/module.py b/src/sage/modules/fp_graded/steenrod/module.py
index 5160ad7351a..21b81b10838 100755
--- a/src/sage/modules/fp_graded/steenrod/module.py
+++ b/src/sage/modules/fp_graded/steenrod/module.py
@@ -941,7 +941,8 @@ class SteenrodFPModule(FPModule):

         elements = [a for a in elements if a not in (0, 1)]

-        profile = enveloping_profile_elements(elements)
+        profile = enveloping_profile_elements(elements,
+                                              char=self.base_ring().characteristic())

         # Avoid returning the zero profile because it triggers a corner case
         # in FPModule.resolution().
diff --git a/src/sage/modules/fp_graded/steenrod/morphism.py b/src/sage/modules/fp_graded/steenrod/morphism.py
index c5be7ba71ff..f35ae0f1117 100755
--- a/src/sage/modules/fp_graded/steenrod/morphism.py
+++ b/src/sage/modules/fp_graded/steenrod/morphism.py
@@ -141,7 +141,8 @@ class SteenrodFPModuleMorphism(FPModuleMorphism):

         elements = [a for a in elements if a not in (0, 1)]

-        profile = enveloping_profile_elements(elements)
+        profile = enveloping_profile_elements(elements,
+                                              char=self.base_ring().characteristic())

         # Avoid returning the zero profile because it triggers a corner case
         # in FPModule.resolution().

Then this happens:

sage: from sage.modules.fp_graded.steenrod.module import SteenrodFPModule
sage: A3 = SteenrodAlgebra(3)
sage: M = SteenrodFPModule(A3, [0], [[A3.P(3)]])
sage: res = M.resolution(3, verbose=True)
Computing f_1 (1/3)
Computing f_2 (2/3)
Resolving the kernel in the range of dimensions [12, 76]: 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76.
Computing f_3 (3/3)
Resolving the kernel in the range of dimensions [44, 116]: 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
~/Desktop/Sage/git/sage/local/var/lib/sage/venv-python3.9/lib/python3.9/site-packages/sage/modules/free_module.py in quotient(self, sub, check)
   4530             try:
-> 4531                 sub = self.subspace(sub)
   4532             except (TypeError, ArithmeticError):

~/Desktop/Sage/git/sage/local/var/lib/sage/venv-python3.9/lib/python3.9/site-packages/sage/modules/free_module.py in subspace(self, gens, check, already_echelonized)
   4089         """
-> 4090         return self.submodule(gens, check=check, already_echelonized=already_echelonized)
   4091 

~/Desktop/Sage/git/sage/local/var/lib/sage/venv-python3.9/lib/python3.9/site-packages/sage/modules/free_module.py in submodule(self, gens, check, already_echelonized)
   3358             if not V.is_submodule(self):
-> 3359                 raise ArithmeticError("Argument gens (= %s) does not generate a submodule of self."%gens)
   3360         return V

TypeError: not all arguments converted during string formatting

During handling of the above exception, another exception occurred:

ArithmeticError                           Traceback (most recent call last)
<ipython-input-4-71aff3686c72> in <module>
----> 1 res = M.resolution(Integer(3), verbose=True)

~/Desktop/Sage/git/sage/local/var/lib/sage/venv-python3.9/lib/python3.9/site-packages/sage/modules/fp_graded/steenrod/module.py in resolution(self, k, top_dim, verbose)
   1050         # Change rings to the finite algebra, and call the base class
   1051         # implementation of this function.
-> 1052         res = FPModule.resolution(
   1053             self.change_ring(finite_algebra),
   1054             k,

~/Desktop/Sage/git/sage/local/var/lib/sage/venv-python3.9/lib/python3.9/site-packages/sage/modules/fp_graded/module.py in resolution(self, k, top_dim, verbose)
   1273 
   1274             f = ret_complex[i-1]
-> 1275             ret_complex.append(f._resolve_kernel(top_dim=top_dim,
   1276                                                  verbose=verbose))
   1277 

~/Desktop/Sage/git/sage/local/var/lib/sage/venv-python3.9/lib/python3.9/site-packages/sage/modules/fp_graded/morphism.py in _resolve_kernel(self, top_dim, verbose)
   1786 
   1787             else:
-> 1788                 Q_n = kernel_n.quotient(j.vector_presentation(n).image())
   1789 
   1790                 if not Q_n.rank():

~/Desktop/Sage/git/sage/local/var/lib/sage/venv-python3.9/lib/python3.9/site-packages/sage/modules/free_module.py in quotient(self, sub, check)
   4531                 sub = self.subspace(sub)
   4532             except (TypeError, ArithmeticError):
-> 4533                 raise ArithmeticError("sub must be a subspace of self")
   4534         A, L = self.__quotient_matrices(sub)
   4535         from . import quotient_module

ArithmeticError: sub must be a subspace of self

If I use M = SteenrodFPModule(A3, [0], [[A3.P(1)]]), everything seems to work, but it fails with SteenrodFPModule(A3, [0], [[A3.P(?)]]) where ? is replaced with any number 3 or larger. Similar at the prime 5: SteenrodFPModule(A5, [0], [[A5.P(5)]]).resolution(3) fails.

jhpalmieri commented 2 years ago
comment:90

To reproduce the problem:

sage: from sage.modules.fp_graded.steenrod.module import SteenrodFPModule
sage: A3 = SteenrodAlgebra(3)
sage: F0 = SteenrodFPModule(A3, [44, 52])
sage: F1 = SteenrodFPModule(A3, [12])
sage: g12 = F1.generator(0)
sage: f = Hom(F0, F1)([A3.P(8)*g12, (A3.P(2,2) + A3.P(6,1))*g12])
sage: f
Module morphism:
  From: Free graded left module on 2 generators over mod 3 Steenrod algebra, milnor basis
  To:   Free graded left module on 1 generator over mod 3 Steenrod algebra, milnor basis
  Defn: g[44] |--> P(8)*g[12]
        g[52] |--> (P(2,2)+P(6,1))*g[12]
sage: f._resolve_kernel()
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
~/Desktop/Sage/git/sage/local/var/lib/sage/venv-python3.9/lib/python3.9/site-packages/sage/modules/free_module.py in quotient(self, sub, check)
   4530             try:
-> 4531                 sub = self.subspace(sub)
   4532             except (TypeError, ArithmeticError):

~/Desktop/Sage/git/sage/local/var/lib/sage/venv-python3.9/lib/python3.9/site-packages/sage/modules/free_module.py in subspace(self, gens, check, already_echelonized)
   4089         """
-> 4090         return self.submodule(gens, check=check, already_echelonized=already_echelonized)
   4091 

~/Desktop/Sage/git/sage/local/var/lib/sage/venv-python3.9/lib/python3.9/site-packages/sage/modules/free_module.py in submodule(self, gens, check, already_echelonized)
   3358             if not V.is_submodule(self):
-> 3359                 raise ArithmeticError("Argument gens (= %s) does not generate a submodule of self."%gens)
   3360         return V

TypeError: not all arguments converted during string formatting

During handling of the above exception, another exception occurred:

ArithmeticError                           Traceback (most recent call last)
<ipython-input-45-219e86205adc> in <module>
----> 1 f._resolve_kernel()

~/Desktop/Sage/git/sage/local/var/lib/sage/venv-python3.9/lib/python3.9/site-packages/sage/modules/fp_graded/steenrod/morphism.py in _resolve_kernel(self, top_dim, verbose)
    360                     g[3, 1] |--> Sq(3)*g[0, 0]
    361         """
--> 362         return self._action(FPModuleMorphism._resolve_kernel, verbose)
    363 
    364 

~/Desktop/Sage/git/sage/local/var/lib/sage/venv-python3.9/lib/python3.9/site-packages/sage/modules/fp_graded/steenrod/morphism.py in _action(self, method, verbose)
    427         # Perform the chosen action on the module after having changed rings
    428         # to the finite algebra.
--> 429         fp_result = method(
    430             self.change_ring(finite_algebra),
    431             verbose=verbose)

~/Desktop/Sage/git/sage/local/var/lib/sage/venv-python3.9/lib/python3.9/site-packages/sage/modules/fp_graded/morphism.py in _resolve_kernel(self, top_dim, verbose)
   1793                     print('im(j):    {}'.format(j.vector_presentation(n).image()))
   1794 
-> 1795                 Q_n = kernel_n.quotient(j.vector_presentation(n).image())
   1796 
   1797                 if not Q_n.rank():

~/Desktop/Sage/git/sage/local/var/lib/sage/venv-python3.9/lib/python3.9/site-packages/sage/modules/free_module.py in quotient(self, sub, check)
   4531                 sub = self.subspace(sub)
   4532             except (TypeError, ArithmeticError):
-> 4533                 raise ArithmeticError("sub must be a subspace of self")
   4534         A, L = self.__quotient_matrices(sub)
   4535         from . import quotient_module

ArithmeticError: sub must be a subspace of self
7ed8c4ca-6d56-4ae9-953a-41e42b4ed313 commented 2 years ago

Branch pushed to git repo; I updated commit sha1. New commits:

e817205trac 30680: documentation edits
7ed8c4ca-6d56-4ae9-953a-41e42b4ed313 commented 2 years ago

Changed commit from 36404aa to e817205

jhpalmieri commented 2 years ago
comment:92

I have not tracked down the odd primary bug, but I did edit the documentation: minor rewordings, modified some lines that were too long, etc. I also fixed the two small bugs mentioned in comment:89.

jhpalmieri commented 2 years ago
comment:93

A very slightly simpler example that fails:

sage: from sage.modules.fp_graded.steenrod.module import SteenrodFPModule
sage: A3 = SteenrodAlgebra(3)
sage: F0 = SteenrodFPModule(A3, [32, 40])
sage: F1 = SteenrodFPModule(A3, [0])
sage: g0 = F1.generator(0)
sage: f = Hom(F0, F1)([A3.P(8)*g0, (A3.P(6,1))*g0])
jhpalmieri commented 2 years ago
comment:94

For what it's worth, I see the same bug if I use the original code posted to this ticket back in the dawn of time.

tscrim commented 2 years ago
comment:95

I want to get rid of the _convert_map function as it is not sustainable if we start adding in other algebra specific FP modules. It also doesn't seem very efficient to me either. Anything I should pay attention to or be careful with this?

I will also look into the comment:90 / comment:93 bug too.

jhpalmieri commented 2 years ago
comment:96

Replying to @tscrim:

I want to get rid of the _convert_map function as it is not sustainable if we start adding in other algebra specific FP modules. It also doesn't seem very efficient to me either. Anything I should pay attention to or be careful with this?

Not that I can think of.

I will also look into the comment:90 / comment:93 bug too.

I wonder if somewhere we're assuming that all nonzero field coefficients are 1, so something gets screwed up in occasional situations in characteristics other than 2. That's just a guess, though. I'm pretty sure that the problem is in _resolve_kernel, in any case.

tscrim commented 2 years ago

Changed branch from u/jhpalmieri/finitely_presented_modules_over_the_steenrod_algebra to u/tscrim/fp_modules_steenrod-30680

tscrim commented 2 years ago
comment:97

Making the FPModule return the corresponding free module when it has no relations has been great for finding bugs and incompatibilities between the two objects. Right now I am at figuring this one out:

sage: A = SteenrodAlgebra(2)
sage: F = A.free_graded_module([0])
sage: G = A.free_graded_module([-1])
sage: H = Hom(G, F)
sage: H._basis_elements(0, False)
Module morphism:
  From: Free graded left module on 1 generator over mod 2 Steenrod algebra, milnor basis
  To:   Free graded left module on 1 generator over mod 2 Steenrod algebra, milnor basis
  Defn: g[-1] |--> 0

The exterior algebra has the same behavior:

sage: E.<x,y,z> = ExteriorAlgebra(QQ)
sage: F = E.free_graded_module([0])
sage: G = E.free_graded_module([-1])
sage: H = Hom(G, F)
sage: H._basis_elements(0, True)
[Module morphism:
   From: Free graded left module on 1 generator over The exterior algebra of rank 3 over Rational Field
   To:   Free graded left module on 1 generator over The exterior algebra of rank 3 over Rational Field
   Defn: g[-1] |--> 0]
sage: H._basis_elements(1, False)
Module morphism:
  From: Free graded left module on 1 generator over The exterior algebra of rank 3 over Rational Field
  To:   Free graded left module on 1 generator over The exterior algebra of rank 3 over Rational Field
  Defn: g[-1] |--> 0
sage: H._basis_elements(1, True)
/home/travis/sage-build/local/lib/python3.9/site-packages/sage/modules/free_module.py:236: UserWarning: You are constructing a free module
over a noncommutative ring. Sage does not have a concept
of left/right and both sided modules, so be careful.
It's also not guaranteed that all multiplications are
done from the right side.
  warn("""You are constructing a free module

The last computation finished quite quickly for A but the last one didn't finish within 1 minute on my laptop (and E is an 8 dimensional skew-commutative algebra!). My guess is this is accidentally passing the algebra somehow rather than the underlying scalar ring for the algebra, which is why we get the warning.

I suspect the fix to the first bug is simply checking if the corresponding morphism is the zero morphism or not.

I also removed the _repr_ as I think it is useful to distinguish when someone has passed something that is secretly free but is not such a class. This is was useful for me to tell when was Hom not being an endomorphism space despite the output suggesting it was. Since basically all things that are free will be a FreeGradedModule (or a subclass), I think this will obtain the print representation you wanted.

There are also a few other tests failing that I don't yet know why.

I am going to get lunch now and do a little bit of other work. I will return to this later today.


New commits:

d231da7Removing _convert_map, docstring and bug fixes, returning free modules when free.
tscrim commented 2 years ago

Changed commit from e817205 to d231da7

tscrim commented 2 years ago

Reviewer: John Palmieri, Travis Scrimshaw

tscrim commented 2 years ago
comment:98

I traced the problem down to this difference in behaviour:

sage: F = FPModule(A, [0], [[]])
sage: Gp = FPModule(A, [-1], [[0]])
sage: Fp = FPModule(A, [0], [[]])
sage: F = E.free_graded_module([0])
sage: G = E.free_graded_module([-1])

sage: F[G.gen(0).degree() + 0]
Vector space of dimension 0 over Rational Field
sage: Fp[Gp.gen(0).degree() + 0]
()

So I can fix the symptom easily enough by not using __getitem__ in the code (which I think is better anyways for the explicitness). However, we should (quickly) decide which behavior we want and change one of these.

7ed8c4ca-6d56-4ae9-953a-41e42b4ed313 commented 2 years ago

Branch pushed to git repo; I updated commit sha1. New commits:

0045cbcFixing issue from different `__getitem__` behavior.
7ed8c4ca-6d56-4ae9-953a-41e42b4ed313 commented 2 years ago

Changed commit from d231da7 to 0045cbc

jhpalmieri commented 2 years ago
comment:100

I agree that we should avoid using __getitem__ in the code. I think if __getitem__(n) should mean anything, then it should mean the nth homogeneous piece as a vector space rather than a basis (is that what it's doing?) for that space.

Re your comment "FIXME: Do not rely on this behavior!" in profile.py: why not? It is documented Python behavior.

tscrim commented 2 years ago
comment:101

Replying to @jhpalmieri:

I agree that we should avoid using __getitem__ in the code. I think if __getitem__(n) should mean anything, then it should mean the nth homogeneous piece as a vector space rather than a basis (is that what it's doing?) for that space.

I agree as well. I will make the corresponding change.

Re your comment "FIXME: Do not rely on this behavior!" in profile.py: why not? It is documented Python behavior.

Yes, but they could decide to change it in a future version. IMO, we should avoid as much as possible treating unordered objects as having a specified order. I think this is more future-proof in case we end up doing some alteration to the dict.

tscrim commented 2 years ago
comment:102

Question regarding the general graded modules: Why do we have a more interesting an_element for the FPModuleHomset that we override for FreeModuleHomset (which is just the zero morphism)? (I recognize that previously they were separate classes, but the underlying question still remains why the free one is a more boring element.)

tscrim commented 2 years ago

Changed commit from 0045cbc to eae4f80

tscrim commented 2 years ago
comment:103

I have fixed a the __getitem__ and a number of other compatibility issues (such as not passing top_dim along). There are still the following:

With regards to the comment:90 bug, it is deeper than the Steenrod algebra:

sage: from sage.modules.fp_graded.module import FPModule
sage: s = SymmetricFunctions(QQ).s()
sage: F = s.free_graded_module([0,0])
sage: L = FPModule(s, [0,0], [[s[3],s[2,1]], [0,s[2]]])
sage: f = Hom(F, L)([L([s[2], 0]), L([0, s[2]])])
sage: f._resolve_kernel()
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
~/sage-build/local/lib/python3.9/site-packages/sage/modules/free_module.py in quotient(self, sub, check)
   4530             try:
-> 4531                 sub = self.subspace(sub)
   4532             except (TypeError, ArithmeticError):

~/sage-build/local/lib/python3.9/site-packages/sage/modules/free_module.py in subspace(self, gens, check, already_echelonized)
   4089         """
-> 4090         return self.submodule(gens, check=check, already_echelonized=already_echelonized)
   4091 

~/sage-build/local/lib/python3.9/site-packages/sage/modules/free_module.py in submodule(self, gens, check, already_echelonized)
   3358             if not V.is_submodule(self):
-> 3359                 raise ArithmeticError("Argument gens (= %s) does not generate a submodule of self."%gens)
   3360         return V

TypeError: not all arguments converted during string formatting

During handling of the above exception, another exception occurred:

ArithmeticError                           Traceback (most recent call last)
<ipython-input-2-f807e560671a> in <module>
      4 L = FPModule(s, [Integer(0),Integer(0)], [[s[Integer(3)],s[Integer(2),Integer(1)]], [Integer(0),s[Integer(2)]]])
      5 f = Hom(F, L)([L([s[Integer(2)], Integer(0)]), L([Integer(0), s[Integer(2)]])])
----> 6 f._resolve_kernel(top_dim=Integer(10))

~/sage-build/local/lib/python3.9/site-packages/sage/modules/fp_graded/morphism.py in _resolve_kernel(self, top_dim, verbose)
   1783 
   1784             else:
-> 1785                 Q_n = kernel_n.quotient(j.vector_presentation(n).image())
   1786 
   1787                 if not Q_n.rank():

~/sage-build/local/lib/python3.9/site-packages/sage/modules/free_module.py in quotient(self, sub, check)
   4531                 sub = self.subspace(sub)
   4532             except (TypeError, ArithmeticError):
-> 4533                 raise ArithmeticError("sub must be a subspace of self")
   4534         A, L = self.__quotient_matrices(sub)
   4535         from . import quotient_module

ArithmeticError: sub must be a subspace of self

I am not sure how to make this smaller. Yet at least it is likely more "simple" because it for an infinite commutative graded algebra of characteristic 0 and exists without this new specific code.


New commits:

6cef6edFixing more bugs, increased compatibilty, added SteenrodFreeModule and morphisms.
eae4f80Making `__getitem__` uniform between free and FP modules.
jhpalmieri commented 2 years ago
comment:104

Regarding "User guide FIXME: Find a better title, possibly (re)move header": I think it is useful to have the previous section "Theoretical background," and so it is useful to have a new section to mark the end of that one. We could call it "Implementation" instead. Other ideas?

Regarding "# FIXME: This seems circular as FPModule builds a morphism": this is in the cokernel_projection method for morphisms. I would be concerned if the __init__ method for morphisms relied on constructing modules and vice versa, but I don't see a problem with this.

It's easy to change profile.py:

diff --git a/src/sage/modules/fp_graded/steenrod/profile.py b/src/sage/modules/fp_graded/steenrod/profile.py
index fa2f78f6d10..679ac0c23af 100755
--- a/src/sage/modules/fp_graded/steenrod/profile.py
+++ b/src/sage/modules/fp_graded/steenrod/profile.py
@@ -228,10 +228,6 @@ def find_min_profile(prof, char=2):
         for i in range(1, max(P)+1):
             if P[i-1] > j and newQ[j] == 2:
                 newQ[i+j] = 2
-    # Convert the dictionary back to a list. As of Python 3.7,
-    # converting values of a dictionary to a list or tuple will result
-    # in the expected order:
-    # https://stackoverflow.com/questions/39980323/are-dictionaries-ordered-in-python-3-6/39980744#39980744
-    # FIXME: Do not rely on this behavior!
-    return (P, tuple(newQ.values()))
-
+    # Convert the dictionary back to a list.
+    Q = [newQ[i] for i in sorted(newQ)]
+    return (P, tuple(Q))

By the way, I tried using all of this with instances of GradedCommutativeAlgebra and ran into problems. It would be nice if we could use the classes here with those graded commutative algebras. (I think I fixed them by defining an alias coefficients = basis_coefficients and defining a rank method for those algebras.) That could wait for a followup ticket.

jhpalmieri commented 2 years ago

Changed branch from u/tscrim/fp_modules_steenrod-30680 to u/jhpalmieri/fp_modules_steenrod-30680

jhpalmieri commented 2 years ago
comment:106

I made the change in profile.py.


New commits:

e014fd8trac 30680: documentation edits, a little cleanup in find_min_profile
jhpalmieri commented 2 years ago

Changed commit from eae4f80 to e014fd8

tscrim commented 2 years ago

Changed branch from u/jhpalmieri/fp_modules_steenrod-30680 to u/tscrim/fp_modules_steenrod-30680

tscrim commented 2 years ago

Changed commit from e014fd8 to 08a92ba

tscrim commented 2 years ago
comment:107

Here is my proposal for the documentation: To move the (very nice) explanation of the modules to its own thematic tutorial. Since it is more of a walkthrough of the capabilities of the code and longer, I felt this was a more natural place for it. In part because the best title I could come up with for that section was "tutorial."

For the #FIXME in the cokernel_projection, I was thinking the morphism that was being created was basically the one defining the cokernel. However this is not the case when the domain is not a free module. So I removed this.

For GCAs, those are not in GradedModulesWithBasis, so it is not so surprising to me they do not work. That being said, they are implemented essentially with a distinguished basis and are already basically in this category. (I have the same gripe about polynomial rings too.) Thus, it should be easy enough to make them work. Another thing I would like to do is rewrite it without using plural, which would likely make it a bit easier to put it in GradedModulesWithBasis.

I tweaked the profile() to avoid going through a dict, so it should be faster (not that it was necessarily a bottleneck before).

I renamed min_presentation() to minimal_presentation() to be more explicit. I don't think we need to deprecate it since this should be merged before the next release. We can setup an (deprecated) alias if we want too.

All that is left is the _resolve_kernel bug I believe (assuming my changes are good).


New commits:

fd174bbRewrite profile() to just use a list.
d6533feMoving the doc to a thematic tutorial.
cb9713cRenaming min_presentation to minimal_presentation.
b055dd8Adding minimal_presentation for free modules.
08a92baSome various cleanup of doc, fixing trivial failures, adding additional doctests.
7ed8c4ca-6d56-4ae9-953a-41e42b4ed313 commented 2 years ago

Changed commit from 08a92ba to 2cc1196

7ed8c4ca-6d56-4ae9-953a-41e42b4ed313 commented 2 years ago

Branch pushed to git repo; I updated commit sha1. New commits:

76ebd8cRemove Fields requirement and add simple test.
2cc1196Adding more support for ZZ (and possibly PID) graded algebras.
tscrim commented 2 years ago
comment:109

I made some additional changes to enable working over PIDs (well, at least ZZ). This was something we claimed to do on the base ticket, but now we have a nontrivial test and can compute resolutions of ZZ-modules. It was some very simple tweaks (the FGP module code could benefit from a cleanup to major refactoring).

Some thoughts on other possible followup tickets (beyond John's suggestion for GCAs):

jhpalmieri commented 2 years ago
comment:110

I think I understand the _resolve_kernel bug, and it's actually in vector_presentation for free modules: when we have a module generator g0 and algebra elements a and b in the same degree, then the module basis in that degree might be (a*g0, b*g0), but sometimes the method ends up using (b*g0, a*g0). So the vectors get screwed up, hence the images of the matrices are wrong. I will try to fix this by being careful about the ordering of the basis in vector_presentation.

jhpalmieri commented 2 years ago
comment:111

Or rather, when extracting the coefficients from (a+b)*g0, the terms might arise in the wrong order because it will use the order from the basis for the algebra, not the order from the basis for the module.

tscrim commented 2 years ago
comment:112

I see. Do you happen to have a (small) example of this behavior?

Slightly related, I am wondering if we need to use dense_coefficient_list() in vector_presentation(). It seems like we just want to use _monomial_coefficients directly since it is using a sparse implementation just unrolled into a list of indices (the sparse_coeffs). There might be other such optimizations that can be done elsewhere. I will wait until you push your fix before doing anything more.

jhpalmieri commented 2 years ago
comment:113

Here is an example:

sage: A31 = SteenrodAlgebra(3, profile=((2,1), ()))
sage: F0 = A31.free_graded_module([36, 44])
sage: F1 = A31.free_graded_module([32, 40])
sage: values = [A31.P(1)*F1.generator(0), A31.P(3)*F1.generator(0) + 2*A31.P(1)*F1.generat
....: or(1)]
sage: j = Hom(F0, F1)(values)
sage: b = [j(x) for x in j.domain().basis_elements(56)][2]
sage: b
(P(2,1)+2P(6))*g[32] + (2P(0,1)+2P(4))*g[40]
sage: a = b.dense_coefficient_list()[0]
sage: a
P(2,1) + 2 P(6)
sage: a.monomials()
[P(6), P(2,1)]
sage: a.coefficients()
[1, 2]

Note the mismatch in order in a.monomials() compared to a.coefficients(). Note also that this works as expected, and in particular the monomials are in a different order:

sage: d = (A31.P(2,1) + 2*A31.P(6)) * F1.generator(0) + (2*A31.P(0,1) + 2*A31.P(4)) * F1.g
....: enerator(1)
sage: c = d.dense_coefficient_list()[0]
sage: c.monomials()
[P(2,1), P(6)]
sage: c.coefficients()
[1, 2]
sage: b == d
True
sage: a == c
True
jhpalmieri commented 2 years ago
comment:114

The problem with using _monomial_coefficients is this: with b as above,

sage: b._monomial_coefficients
{32: P(2,1) + 2 P(6), 40: 2 P(0,1) + 2 P(4)}

We need to match up P(2,1) * g[32], P(6) * g[32], etc., with the module basis elements in that dimension, so we have to split up the values anyway. We might be able to use _monomial_coefficients, but it's not entirely trivial.