sagemath / sage

Main repository of SageMath
https://www.sagemath.org
Other
1.46k stars 485 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 3 years ago
comment:40

By the way, if we wanted separate classes for degree zero maps and maps of arbitrary degree, then vector_presentation would make sense for the zero map in the degree zero case: you would just get the zero matrix of the appropriate size.

jhpalmieri commented 3 years ago
comment:41

A more practical question: if we don't allow maps of nonzero degree in Hom, then how do we construct maps? Currently the easiest way to construct them is Hom(M,N)(...values...), but that won't work if we change Hom. Hom does take an extra argument, category, so we could use a different category to allow nonzero degree maps, although I'm not sure what that category should be called.

tscrim commented 3 years ago
comment:42

Replying to @dimpase:

maybe sage.combinat.free_module ought to be extended to accommodate for the needs of this ticket?

+1 and we can likely use a lot of the stuff from the category, which means just implementing stuff at the parent level. It might be a bit slower than directly implementing it, but it might not be the bottleneck.

I find it surprising that sage.combinat.free_module has no concept of grading.

It is all in the category, which get then you implement for the corresponding parent. Perhaps we could extend CFM that takes an optional grading parameter, but so far, every use-case of this required us to subclass for other functionality too.

jhpalmieri commented 3 years ago
comment:43

Replying to @tscrim:

Replying to @dimpase:

maybe sage.combinat.free_module ought to be extended to accommodate for the needs of this ticket?

+1 and we can likely use a lot of the stuff from the category, which means just implementing stuff at the parent level. It might be a bit slower than directly implementing it, but it might not be the bottleneck.

Although CombinatorialFreeModule is not exactly right for the modules here. At least in the free module case (which is as far as I've gotten), you only care about the degrees of the generators, you don't want to give them names, so there is no named basis. A typical element is listed as <a1, a2, ...> which means the sum of a_i * generator_i, and this looks like a pretty efficient way to describe elements. We could create names, but they would be artificial.

Note that sage.modules.free_module is not suited for this, either: if you try to use it to create a free module over, say, the Steenrod algebra, you get

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.

I find it surprising that sage.combinat.free_module has no concept of grading.

It is all in the category, which get then you implement for the corresponding parent. Perhaps we could extend CFM that takes an optional grading parameter, but so far, every use-case of this required us to subclass for other functionality too.

tscrim commented 3 years ago
comment:44

Replying to @jhpalmieri:

This may be more a question for Travis. Should the category be GradedModules or GradedModulesWithBasis?

If there is a distinguished basis for the module, then GradedModulesWithBasis. Otherwise just GradedModules.

Maybe if we're working over an algebra with a distinguished basis, the module should inherit that basis, at least in the case of a free module.

I would think that depends on the construction. However, having a distinguished basis means you can do linear algebra, which gives you a lot more functionality.

I'm not sure, though, and I really don't know what to do about non-free modules. (I haven't gotten that far in my modifications: I've just been dealing with free modules.)

Since we don't have much if any support for nonfree modules in Sage, you are out there on your own to figure out what the best practice should be.

I'm a little puzzled by the category setup in this context. Does "with basis" refer to a basis over the algebra over which we're working, or over the base field for that algebra? Consider this:

sage: R.<x> = QQ[]                                                                       
sage: R                                                                                  
Univariate Polynomial Ring in x over Rational Field
sage: M = FreeModule(R, ['a', 'b'])                                                      
sage: M.category()                                                                       
Category of finite dimensional modules with basis over Univariate Polynomial Ring in x over Rational Field

The "finite dimensional" part of that sounds odd unless you interpret it as meaning "free and finitely generated".

Perhaps it would be a bit better to call it the category of "finite rank free modules", but that is something you are best discussing with Nicholas about.

sage: M.basis()
Finite family {'a': B['a'], 'b': B['b']}

So this is viewing the basis as being over the polynomial ring.

Agreed.

On the other hand:

sage: A = GradedModulesWithBasis(QQ).example()                                           
sage: A                                                                                  
An example of a graded module with basis: the free module on partitions over Rational Field
sage: A.homogeneous_component_basis(4)                                                   
Lazy family (Term map from Partitions to An example of a graded module with basis: the free module on partitions over Rational Field(i))_{i in Partitions of the integer 4}
sage: list(A.homogeneous_component_basis(4))                                             
[P[4], P[3, 1], P[2, 2], P[2, 1, 1], P[1, 1, 1, 1]]

So homogeneous_component_basis(...) allows access to the vector space basis.

It looks like "with basis" means over the ring or algebra connected to the module, not the ground field.

Yes, that is correct, it is the base ring R, not any underlying ground ring/field that might be used to construct R.

So maybe it should be in the categories

  • GradedModulesWithBasis(A) only in the case of free modules
  • GradedModulesWithBasis(A.base_ring()) for all modules, assuming we can determine a basis of each homogeneous piece, and this should be possible as long as the algebra has a distinguished basis. Does that make sense?

This sounds like more of a question with the underlying mathematics that I can't answer.

tscrim commented 3 years ago
comment:45

Replying to @jhpalmieri:

Replying to @tscrim:

Replying to @dimpase:

maybe sage.combinat.free_module ought to be extended to accommodate for the needs of this ticket?

+1 and we can likely use a lot of the stuff from the category, which means just implementing stuff at the parent level. It might be a bit slower than directly implementing it, but it might not be the bottleneck.

Although CombinatorialFreeModule is not exactly right for the modules here. At least in the free module case (which is as far as I've gotten), you only care about the degrees of the generators, you don't want to give them names, so there is no named basis. A typical element is listed as <a1, a2, ...> which means the sum of a_i * generator_i, and this looks like a pretty efficient way to describe elements. We could create names, but they would be artificial.

I don't think there is anything wrong with having an artificial name for the basis elements. This is just implicit in Sage's R^n version of free modules, but we give a name to the basis all the time in math once we want to start working with a basis. However, using CFM is generally slower because its implementation is in Python, always sparse, and has some extra overhead because it allows more generic basis indices. Yet, you do gain the ability to handle infinite dimensional objects.

Note that sage.modules.free_module is not suited for this, either: if you try to use it to create a free module over, say, the Steenrod algebra, you get

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.

Let S be the Steenrod algebra over a (commutative) ring R. Perhaps we should think of the free module as a representation of S over R, something you alluded to in your other comments. Internally, this seems to be how it is storing elements, as an infinite dimensional R-module

tscrim commented 3 years ago
comment:46

Replying to @jhpalmieri:

A more practical question: if we don't allow maps of nonzero degree in Hom, then how do we construct maps? Currently the easiest way to construct them is Hom(M,N)(...values...), but that won't work if we change Hom. Hom does take an extra argument, category, so we could use a different category to allow nonzero degree maps, although I'm not sure what that category should be called.

Strictly speaking, I think a map that shifts degree should not live in the category of graded modules. You are always fine using a larger category, such as the category of modules. However, we could potentially allow a slight abuse here and extend the correspondingHomset to take nonzero degree maps.

jhpalmieri commented 3 years ago
comment:47

For now I am going to allow maps of nonzero degree, just because it makes it much easier to construct such maps.

jhpalmieri commented 3 years ago
comment:48

I've created #32505 and I will push a branch there soon.

jhpalmieri commented 3 years ago
comment:49

Comments on some changes made from here to #32505:

jhpalmieri commented 3 years ago

Dependencies: #32505

jhpalmieri commented 3 years ago
comment:50

Marking as "needs work" because we should deal with #32505 first, then build this on top of that.

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

Branch pushed to git repo; I updated commit sha1. This was a forced push. Last 10 new commits:

72e455eWe do not require the base algebra's base ring to be a field.
11b3725trac 32505: allow the resolution to be made up of maps between
fcce24dMerge branch 'public/modules/free_graded_modules-32505' of git://trac.sagemath.org/sage into public/modules/free_graded_modules-32505
2e0518fUsing free modules where possible and improved compatibility.
6c1ab8dtrac 32505: more fixes
6cf6d14Merge branch 'public/modules/free_graded_modules-32505' of git://trac.sagemath.org/sage into public/modules/free_graded_modules-32505
5074464Some last fixes and removing redundancy.
ffe7179trac 32505: replace "PlusInfinity()" with "infinity"
a1a9467trac 32505: remove redundant "zero" and "identity" methods
4ed74b9trac 30680: rebased on top of 32505.
7ed8c4ca-6d56-4ae9-953a-41e42b4ed313 commented 2 years ago

Changed commit from 6a617ff to 4ed74b9

jhpalmieri commented 2 years ago
comment:53

Here is a branch so that we can start working on this, now that #32505 is positively reviewed. I did some clean-up on the old code, and it's almost all working, but not quite. I think that the problem is this: morphisms between finitely presented modules over the Steenrod algebra (the class of modules is currently called FPA_Module) is not quite behaving the way I want, and in particular, some of the time, domains and codomains of such morphisms are not in the class FPA_Module anymore. It is important that they be in this class because the code is using special properties of the Steenrod algebra to determine, for example, whether a morphism is injective: given two finitely presented modules, one can find a finite-dimensional subalgebra B such that a morphism as A-modules is injective if and only if it is injective as a B-module map. The case over the finite subalgebra is tractable.

But if we can't remember where the objects live, then we lose these special methods for A-modules, and so we get doctest failures. I'm seeing a few failures in module.py, I think all due to this.

By the way, I rewrote lots of profile.py and added a few doctests for odd primes, although we don't have any odd primary examples in the rest of the code. The new version of find_min_profile is about 10 times faster than the old version. When I get around to it, I will try some odd primary examples of modules and morphisms. Should I expect those to work?

jhpalmieri commented 2 years ago
comment:54

The first two doctest failures:

sage -t --random-seed=52206228944996655655996976540735053542 src/sage/modules/fp_graded/steenrod/module.py
**********************************************************************
File "src/sage/modules/fp_graded/steenrod/module.py", line 484, in sage.modules.fp_graded.steenrod.module
Failed example:
    h.is_injective()        # Is ker(f) contained in K ?
Exception raised:
    Traceback (most recent call last):
      File "/Users/palmieri/Desktop/Sage/sage_builds/TESTING/sage-9.5/local/var/lib/sage/venv-python3.9/lib/python3.9/site-packages/sage/doctest/forker.py", line 694, in _run
        self.compile_and_execute(example, compiler, test.globs)
      File "/Users/palmieri/Desktop/Sage/sage_builds/TESTING/sage-9.5/local/var/lib/sage/venv-python3.9/lib/python3.9/site-packages/sage/doctest/forker.py", line 1088, in compile_and_execute
        exec(compiled, globs)
      File "<doctest sage.modules.fp_graded.steenrod.module[94]>", line 1, in <module>
        h.is_injective()        # Is ker(f) contained in K ?
      File "/Users/palmieri/Desktop/Sage/sage_builds/TESTING/sage-9.5/local/var/lib/sage/venv-python3.9/lib/python3.9/site-packages/sage/modules/fp_graded/morphism.py", line 1634, in is_injective
        j0 = self._resolve_kernel(top_dim, verbose)
      File "/Users/palmieri/Desktop/Sage/sage_builds/TESTING/sage-9.5/local/var/lib/sage/venv-python3.9/lib/python3.9/site-packages/sage/modules/fp_graded/morphism.py", line 1752, in _resolve_kernel
        raise ValueError('a top dimension must be specified for this calculation to terminate')
    ValueError: a top dimension must be specified for this calculation to terminate
**********************************************************************
File "src/sage/modules/fp_graded/steenrod/module.py", line 540, in sage.modules.fp_graded.steenrod.module
Failed example:
    res = Hko.resolution(6, verbose=True)
Exception raised:
    Traceback (most recent call last):
      File "sage/misc/cachefunc.pyx", line 1943, in sage.misc.cachefunc.CachedMethodCaller.__call__ (build/cythonized/sage/misc/cachefunc.c:10347)
        return cache[k]
    KeyError: ((2,), ())

    During handling of the above exception, another exception occurred:

    Traceback (most recent call last):
      File "/Users/palmieri/Desktop/Sage/sage_builds/TESTING/sage-9.5/local/var/lib/sage/venv-python3.9/lib/python3.9/site-packages/sage/modules/vector_space_homspace.py", line 395, in __call__
        v = [C(a) for a in A]
      File "/Users/palmieri/Desktop/Sage/sage_builds/TESTING/sage-9.5/local/var/lib/sage/venv-python3.9/lib/python3.9/site-packages/sage/modules/vector_space_homspace.py", line 395, in <listcomp>
        v = [C(a) for a in A]
      File "sage/structure/parent.pyx", line 898, in sage.structure.parent.Parent.__call__ (build/cythonized/sage/structure/parent.c:9388)
        return mor._call_(x)
      File "sage/structure/coerce_maps.pyx", line 161, in sage.structure.coerce_maps.DefaultConvertMap_unique._call_ (build/cythonized/sage/structure/coerce_maps.c:4665)
        raise
      File "sage/structure/coerce_maps.pyx", line 156, in sage.structure.coerce_maps.DefaultConvertMap_unique._call_ (build/cythonized/sage/structure/coerce_maps.c:4557)
        return C._element_constructor(x)
      File "/Users/palmieri/Desktop/Sage/sage_builds/TESTING/sage-9.5/local/var/lib/sage/venv-python3.9/lib/python3.9/site-packages/sage/modules/free_module.py", line 5769, in _element_constructor_
        return FreeModule_generic_field._element_constructor_(self, e, *args, **kwds)
      File "/Users/palmieri/Desktop/Sage/sage_builds/TESTING/sage-9.5/local/var/lib/sage/venv-python3.9/lib/python3.9/site-packages/sage/modules/free_module.py", line 1118, in _element_constructor_
        return self.element_class(self, x, coerce, copy)
      File "sage/modules/vector_mod2_dense.pyx", line 200, in sage.modules.vector_mod2_dense.Vector_mod2_dense.__init__ (build/cythonized/sage/modules/vector_mod2_dense.cpp:3952)
        raise TypeError("x must be a list of the right length")
    TypeError: x must be a list of the right length

    During handling of the above exception, another exception occurred:

    Traceback (most recent call last):
      File "/Users/palmieri/Desktop/Sage/sage_builds/TESTING/sage-9.5/local/var/lib/sage/venv-python3.9/lib/python3.9/site-packages/sage/doctest/forker.py", line 694, in _run
        self.compile_and_execute(example, compiler, test.globs)
      File "/Users/palmieri/Desktop/Sage/sage_builds/TESTING/sage-9.5/local/var/lib/sage/venv-python3.9/lib/python3.9/site-packages/sage/doctest/forker.py", line 1088, in compile_and_execute
        exec(compiled, globs)
      File "<doctest sage.modules.fp_graded.steenrod.module[105]>", line 1, in <module>
        res = Hko.resolution(Integer(6), verbose=True)
      File "/Users/palmieri/Desktop/Sage/sage_builds/TESTING/sage-9.5/local/var/lib/sage/venv-python3.9/lib/python3.9/site-packages/sage/modules/fp_graded/steenrod/module.py", line 1049, in resolution
        res = FPModule.resolution(
      File "/Users/palmieri/Desktop/Sage/sage_builds/TESTING/sage-9.5/local/var/lib/sage/venv-python3.9/lib/python3.9/site-packages/sage/modules/fp_graded/module.py", line 1269, in resolution
        ret_complex.append(f._resolve_kernel(top_dim=top_dim,
      File "/Users/palmieri/Desktop/Sage/sage_builds/TESTING/sage-9.5/local/var/lib/sage/venv-python3.9/lib/python3.9/site-packages/sage/modules/fp_graded/morphism.py", line 1768, in _resolve_kernel
        self_n = self.vector_presentation(n)
      File "sage/misc/cachefunc.pyx", line 1948, in sage.misc.cachefunc.CachedMethodCaller.__call__ (build/cythonized/sage/misc/cachefunc.c:10483)
        w = self._instance_call(*args, **kwds)
      File "sage/misc/cachefunc.pyx", line 1824, in sage.misc.cachefunc.CachedMethodCaller._instance_call (build/cythonized/sage/misc/cachefunc.c:9949)
        return self.f(self._instance, *args, **kwds)
      File "/Users/palmieri/Desktop/Sage/sage_builds/TESTING/sage-9.5/local/var/lib/sage/venv-python3.9/lib/python3.9/site-packages/sage/modules/fp_graded/morphism.py", line 913, in vector_presentation
        return Hom(D_n, C_n)([C_n.zero() if e.is_zero() else e.vector_presentation()
      File "/Users/palmieri/Desktop/Sage/sage_builds/TESTING/sage-9.5/local/var/lib/sage/venv-python3.9/lib/python3.9/site-packages/sage/modules/vector_space_homspace.py", line 399, in __call__
        raise ArithmeticError(msg)
    ArithmeticError: some proposed image is not in the codomain, because
    x must be a list of the right length

(There are others which fail because res is not defined after line 540 fails.)

The first failure is definitely because of the issue I mentioned above: the code should auto-detect an appropriate finite-dimensional algebra to work over, in which case we should not have to specify a top dimension, but clearly something is going wrong. I think that one of the morphisms is in the class FPMorphism rather than the Steenrod-specific FPA_Morphism. An example:

sage: from sage.modules.fp_graded.steenrod.module import FPA_Module
sage: A = SteenrodAlgebra()
sage: Hko = FPA_Module(A, [0], [[Sq(2)], [Sq(1)]])
sage: gen = Hko.generator(0)
sage: homspace = Hom(Hko, Hko)
sage: values = [Sq(0, 0, 1)*gen]
sage: f = homspace(values)

sage:  # Good so far:

sage: type(f)
<class 'sage.modules.fp_graded.steenrod.homspace.FPA_ModuleHomspace_with_category_with_equality_by_id.element_class'>
sage: k = f.kernel_inclusion()
sage: type(k)
<class 'sage.modules.fp_graded.steenrod.homspace.FPA_ModuleHomspace_with_category_with_equality_by_id.element_class'>
sage: coker = k.cokernel_projection()
sage: type(coker)
<class 'sage.modules.fp_graded.steenrod.homspace.FPA_ModuleHomspace_with_category_with_equality_by_id.element_class'>

sage: # But this is a problem:

sage: type(coker.codomain())
<class 'sage.modules.fp_graded.module.FPModule_with_category'>

It should be FPA_Module_with_category. I don't know why this codomain is not in the right class.

jhpalmieri commented 2 years ago
comment:55

I can fix the first problem by defining a method cokernel_projection for FPA_Morphism, just reproducing the code for FPMorphism. I don't know if there is a better solution. I'm now working on the other doctest failure.

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

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

a9741cbtrac 30680: bug fixes
7ed8c4ca-6d56-4ae9-953a-41e42b4ed313 commented 2 years ago

Changed commit from 4ed74b9 to a9741cb

jhpalmieri commented 2 years ago
comment:57

Okay, I found a bug and fixed it, and that took care of most of the problems. Something like the first issue I mentioned is still occurring: I don't know how to naturally force a morphism to be in the right class. The remaining failure comes from this doctest:

    sage: def is_exact(res):
    ....:     for i in range(len(res)-1):
    ....:         h = res[i].homology(res[i+1])
    ....:         if not h.codomain().is_trivial():
    ....:             return False
    ....:     return True

    sage: is_exact(res)
    True

The problem is that the maps in res are maps of FreeGradedModules, so they are not in the Steenrod-specific world, so the homology computation doesn't work.


In case you're interested, the bug is a subtle one in the code from #32505: to compute the coefficients of an element in a free module, we were using

        return self.dense_coefficient_list()

but the default ordering for the coefficients is in increasing order of degree, not the specified order for the generators. We were hitting a situation in which this mattered, and changing to this fixed it:

        order = self.parent()._indices
        return self.dense_coefficient_list(order)

I will add some doctests that capture this.

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

Changed commit from a9741cb to 67e78ed

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

Branch pushed to git repo; I updated commit sha1. This was a forced push. New commits:

67e78edtrac 30680: bug fixes
7ed8c4ca-6d56-4ae9-953a-41e42b4ed313 commented 2 years ago

Branch pushed to git repo; I updated commit sha1. This was a forced push. New commits:

fb130d2trac 30680: bug fixes
7ed8c4ca-6d56-4ae9-953a-41e42b4ed313 commented 2 years ago

Changed commit from 67e78ed to fb130d2

jhpalmieri commented 2 years ago
comment:60

We should now be at 100% coverage and one doctest failure. I'm sure there are typos and plenty of room for other improvements, but just about everything is now working. I still haven't tried odd primes.

tscrim commented 2 years ago
comment:61

One quick thought is to add an attribute or someway to let the morphism code know what type of modules to create. It might work by using type(self.codomain()) or similar (with perhaps some care to make sure it is not the category-mangled object). This would avoid a lot of code and documentation duplication with making it more future-proof.

I am quite surprised by this bug and slightly worried because it should not matter about the order (as long as internally each class knows how to go back and forth between consistently).

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

Branch pushed to git repo; I updated commit sha1. This was a forced push. New commits:

bf9fcdbtrac 30680: bug fixes
7ed8c4ca-6d56-4ae9-953a-41e42b4ed313 commented 2 years ago

Changed commit from fb130d2 to bf9fcdb

jhpalmieri commented 2 years ago
comment:63

I wonder if there is some simple modification to the code for resolution for Steenrod algebra modules:

    def resolution(self, k, top_dim=None, verbose=False):
        """
        (documentation omitted)
        """
        algebra = self.base_ring()
        finite_algebra = SteenrodAlgebra_generic(algebra.prime(), profile=self.profile())

        # Change rings to the finite algebra, and call the base class
        # implementation of this function.
        res = FPModule.resolution(
            self.change_ring(finite_algebra),
            k,
            top_dim=top_dim,
            verbose=verbose)

        # Change rings back to the original Steenrod algebra.
        return [j.change_ring(self.base_ring()) for j in res]

Something at the end that does more than just change the ring, but also forces the morphism to have the right class. This code explicitly calls FPModule.resolution, and that's probably the root of the problem. We could completely duplicate the general resolution code to the Steenrod algebra setting, but there must be a better way.

By the way, although A is a standard thing for homotopy theorists to call the Steenrod algebra, FPA_Module is not the most evocative name, I think. SteenrodModule? SteenrodFPModule? What should we call the class? (Have to get a jump on the bike-shedding.)

jhpalmieri commented 2 years ago
comment:64

Replying to @jhpalmieri:

I wonder if there is some simple modification to the code for resolution for Steenrod algebra modules:

Something at the end that does more than just change the ring, but also forces the morphism to have the right class. This code explicitly calls FPModule.resolution, and that's probably the root of the problem.

We could, for example, change each map by reconstructing each domain and codomain, using FPA_Module to instantiate them, and then reconstructing each morphism. It should be easy to write a method or a private function that does this.

jhpalmieri commented 2 years ago
comment:65

Replying to @tscrim:

I am quite surprised by this bug and slightly worried because it should not matter about the order (as long as internally each class knows how to go back and forth between consistently).

Indeed, my "fix" has broken something. This works:

sage: from sage.modules.fp_graded.free_module import FreeGradedModule
sage: F.<x,y> = FreeGradedModule(A, (2, 0))
sage: x.degree()
2
sage: y.degree()
0

This doesn't:

sage: from sage.modules.fp_graded.module import FPModule
sage: A = SteenrodAlgebra()
sage: M.<x,y> = FPModule(A, (2, 0))
sage: x.degree()
0
sage: y.degree()
2

To compute the degree of an element of an FPModule, it first computes a lift to a free module and then computes the degree there, but when computing that lift, it mixes up the order of the generators.

sage: x.lift_to_free()
y

This is somehow due to imposing the order in coefficients.

jhpalmieri commented 2 years ago
comment:66

This fixes some of these problems, although I think I still need to specify the order in dense_coefficient_list:

diff --git a/src/sage/modules/fp_graded/element.py b/src/sage/modules/fp_graded/element.py
index 87f52b759a..51d48f405e 100755
--- a/src/sage/modules/fp_graded/element.py
+++ b/src/sage/modules/fp_graded/element.py
@@ -122,7 +122,7 @@ class FPElement(IndexedFreeModuleElement):
             sage: y.coefficients()
             [0, Sq(2)]
         """
-        return [self[i] for i in sorted(self.parent().indices())]
+        return [self[i] for i in self.parent().indices()]

     def _lmul_(self, a):
jhpalmieri commented 2 years ago
comment:67

Let's work on the bugs in finitely presented modules in #33275 (I hope quickly) and then deal with the issues specific to the Steenrod algebra here.

jhpalmieri commented 2 years ago

Changed dependencies from #32505 to #32505, #33275

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

Changed commit from bf9fcdb to 2fd5cfd

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

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

e3d4234trac 33275: bug fixes for finitely presented graded modules.
2fd5cfdtrac 30680: rebased on top of #33275
7ed8c4ca-6d56-4ae9-953a-41e42b4ed313 commented 2 years ago

Branch pushed to git repo; I updated commit sha1. This was a forced push. New commits:

ba19b0btrac 30680: rebased on top of #33275
7ed8c4ca-6d56-4ae9-953a-41e42b4ed313 commented 2 years ago

Changed commit from 2fd5cfd to ba19b0b

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

Changed commit from ba19b0b to 9a3b01e

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

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

f9ff665trac 33275: change "coefficients" to "dense_coefficient_list"
9a3b01eMerge branch 't/33275/fp-module-bugs' into trac30680
7ed8c4ca-6d56-4ae9-953a-41e42b4ed313 commented 2 years ago

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

47b3ce8trac 30680: use dense_coefficient_list
7ed8c4ca-6d56-4ae9-953a-41e42b4ed313 commented 2 years ago

Changed commit from 9a3b01e to 47b3ce8

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

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

f550b61trac 33275: use linear_combination in a few more places
dd2f7ebMerge branch 't/33275/fp-module-bugs' into trac30680
9e91198trac 30680: typo
7ed8c4ca-6d56-4ae9-953a-41e42b4ed313 commented 2 years ago

Changed commit from 47b3ce8 to 9e91198

tscrim commented 2 years ago
comment:74

I don't know for your other questions off the top of my head (I am out the door to go back home now), but I am a big +1 for renaming to something like SteenrodFPModule. I will think about the other questions and reply tomorrow.

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

Changed commit from 9e91198 to a2dc039

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

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

a2dc039trac 30680: use "SteenrodFPModule" etc for class names
tscrim commented 2 years ago
comment:76

Thank you.

Okay, so I have two ways out of this issue of not having the correct class specified when building, e.g., the resolution:

  1. We introduce a method, say, _graded_module_class() (that takes a parameter free) that returns the corresponding class.
  2. We use type(self.codomain()) and attach, via an attribute, the free module class to each FP module.

I am maybe slightly preferential towards 2 since it is local to the module implementation (which is what needs this information) rather than more global to the algebras. We might have some consistency issues to address (such as the free modules not taking a morphism as defining information), but these will likely improve the overall quality and robustness of the code.

jhpalmieri commented 2 years ago
comment:77

To make sure we're on the same page, there are (I think) two problems with resolution: (a) if M is an instance of SteenrodFPModule, then M.resolution(...) forgets that and returns morphisms that come from FPModule (or FreeGradedModule) rather than SteenrodFPModule. Then (b) if I fix that by converting all of the morphism back to SteenrodFPModuleMorphism, the resulting objects are not recognized as free, so for example they will be described in the string representation as being "finitely presented."

I think that since we used FreeGradedModule more than was originally planned by the authors in #32505, we may now need SteenrodFreeModule and similar classes for morphisms and homspaces.

jhpalmieri commented 2 years ago
comment:78

Here is what I'm currently using to convert the maps:

diff --git a/src/sage/modules/fp_graded/steenrod/module.py b/src/sage/modules/fp_graded/steenrod/module.py
index c74ed1535ca..9a19a87f65f 100755
--- a/src/sage/modules/fp_graded/steenrod/module.py
+++ b/src/sage/modules/fp_graded/steenrod/module.py
@@ -827,6 +827,7 @@ AUTHORS:
 #                  https://www.gnu.org/licenses/
 # ****************************************************************************

+from sage.categories.homset import Hom
 from sage.rings.infinity import infinity
 from sage.algebras.steenrod.steenrod_algebra import SteenrodAlgebra_generic
 from sage.modules.fp_graded.module import FPModule
@@ -1056,7 +1057,7 @@ class SteenrodFPModule(FPModule):
             verbose=verbose)

         # Change rings back to the original Steenrod algebra.
-        return [j.change_ring(self.base_ring()) for j in res]
+        return [convert_map(j.change_ring(self.base_ring())) for j in res]

     def export_module_definition(self, powers_of_two_only=True):
@@ -1181,3 +1182,21 @@ class SteenrodFPModule(FPModule):
                             len(values),
                             " ".join(["%d" % x for x in values])))
                     element_index += 1
+
+
+def convert_map(f):
+    if f.domain().has_relations():
+        D = SteenrodFPModule(f.domain()._j)
+    else:
+        try:
+            D = SteenrodFPModule(f.domain()._free_module())
+        except AttributeError:
+            D = SteenrodFPModule(f.domain().base_ring(), f.domain()._generator_degrees)
+    if f.codomain().has_relations():
+        C = SteenrodFPModule(f.codomain()._j)
+    else:
+        try:
+            C = SteenrodFPModule(f.codomain()._free_module())
+        except AttributeError:
+            C = SteenrodFPModule(f.codomain().base_ring(), f.codomain()._generator_degrees)
+    return Hom(D, C)(f.values())