Skip to content

Commit

Permalink
Trac #33275: Bug fixes for finitely presented graded modules (#32505)
Browse files Browse the repository at this point in the history
Some bug fixes for the ticket #32505.

URL: https://trac.sagemath.org/33275
Reported by: jhpalmieri
Ticket author(s): John Palmieri
Reviewer(s): Travis Scrimshaw
  • Loading branch information
Release Manager committed Feb 14, 2022
2 parents 2e1f8d2 + 29dbd23 commit d026799
Show file tree
Hide file tree
Showing 7 changed files with 116 additions and 35 deletions.
33 changes: 27 additions & 6 deletions src/sage/modules/fp_graded/element.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ def lift_to_free(self):
Free graded left module on 2 generators over mod 2 Steenrod algebra, milnor basis
"""
C = self.parent()._j.codomain()
return C(self.coefficients())
return C(self.dense_coefficient_list())


@cached_method
Expand Down Expand Up @@ -100,29 +100,48 @@ def degree(self):
Traceback (most recent call last):
...
ValueError: the zero element does not have a well-defined degree
Testing that the order of the coefficients agrees with the
order of the generators::
sage: F.<x,y> = FPModule(SteenrodAlgebra(), (2, 0))
sage: x.degree()
2
sage: y.degree()
0
"""
if self.is_zero():
raise ValueError("the zero element does not have a well-defined degree")
return self.lift_to_free().degree()


def coefficients(self):
def dense_coefficient_list(self, order=None):
"""
Return a list of all coefficients of ``self``.
INPUT:
- ``order`` -- (optional) an ordering of the basis indexing set
Note that this includes *all* of the coefficients, not just
the nonzero ones. By default they appear in the same order as
the module generators.
EXAMPLES::
sage: from sage.modules.fp_graded.module import FPModule
sage: A = SteenrodAlgebra()
sage: M = FPModule(SteenrodAlgebra(2), [0,1], [[Sq(4), Sq(3)]])
sage: x = M([Sq(1), 1])
sage: x.coefficients()
sage: x.dense_coefficient_list()
[Sq(1), 1]
sage: y = Sq(2) * M.generator(1)
sage: y.coefficients()
sage: y.dense_coefficient_list()
[0, Sq(2)]
"""
return [self[i] for i in sorted(self.parent().indices())]
if order is None:
order = self.parent()._indices
return [self[i] for i in order]


def _lmul_(self, a):
Expand Down Expand Up @@ -211,7 +230,9 @@ def vector_presentation(self):
sage: basis = M.basis_elements(7)
sage: x_ = sum( [c*b for (c,b) in zip(v, basis)] ); x_
Sq(0,0,1)*m0 + Sq(3,1)*m1
sage: x == x_
sage: x__ = M.linear_combination(zip(basis, v)); x__
Sq(0,0,1)*m0 + Sq(3,1)*m1
sage: x == x_ == x__
True
TESTS::
Expand Down
38 changes: 31 additions & 7 deletions src/sage/modules/fp_graded/free_element.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@

from sage.misc.cachefunc import cached_method
from sage.modules.with_basis.indexed_element import IndexedFreeModuleElement
from sage.categories.finite_dimensional_modules_with_basis import FiniteDimensionalModulesWithBasis

class FreeGradedModuleElement(IndexedFreeModuleElement):
r"""
Expand All @@ -49,21 +50,40 @@ class FreeGradedModuleElement(IndexedFreeModuleElement):
Sq(1)*g[0] + g[1]
"""

def coefficients(self):
def dense_coefficient_list(self, order=None):
"""
Return a list of all coefficients of ``self``.
INPUT:
- ``order`` -- (optional) an ordering of the basis indexing set
Note that this includes *all* of the coefficients, not just
the nonzero ones. By default they appear in the same order as
the module generators.
EXAMPLES::
sage: from sage.modules.fp_graded.free_module import FreeGradedModule
sage: A = SteenrodAlgebra()
sage: M.<Y,Z> = FreeGradedModule(SteenrodAlgebra(2), (0, 1))
sage: x = M.an_element(7); x
Sq(0,0,1)*Y + Sq(3,1)*Z
sage: x.coefficients()
sage: x.dense_coefficient_list()
[Sq(0,0,1), Sq(3,1)]
TESTS:
A module with generators in the "wrong" order::
sage: M.<Y,Z> = FreeGradedModule(SteenrodAlgebra(2), (1, 0))
sage: a = Sq(0,0,1)*Y + Sq(3,1)*Z
sage: a.dense_coefficient_list()
[Sq(0,0,1), Sq(3,1)]
"""
return self.dense_coefficient_list()
if order is None:
order = self.parent()._indices
return super().dense_coefficient_list(order)


def degree(self):
Expand Down Expand Up @@ -104,7 +124,8 @@ def degree(self):
raise ValueError("the zero element does not have a well-defined degree")
degrees = []
try:
for g, c in zip(self.parent().generator_degrees(), self.coefficients()):
for g, c in zip(self.parent().generator_degrees(),
self.dense_coefficient_list()):
if c:
degrees.append(g + c.degree())
except ValueError:
Expand Down Expand Up @@ -174,7 +195,7 @@ def _lmul_(self, a):
Sq(1,1,1)*x0 + Sq(1,1,1)*y0 + Sq(5,1)*z3,
Sq(3,2)*z3]
"""
return self.parent()((a*c for c in self.coefficients()))
return self.parent()((a*c for c in self.dense_coefficient_list()))

@cached_method
def vector_presentation(self):
Expand Down Expand Up @@ -222,7 +243,9 @@ def vector_presentation(self):
sage: basis = M.basis_elements(7)
sage: x_ = sum( [c*b for (c,b) in zip(v, basis)] ); x_
Sq(0,0,1)*g[0] + Sq(3,1)*g[1]
sage: x == x_
sage: x__ = M.linear_combination(zip(basis, v)); x__
Sq(0,0,1)*g[0] + Sq(3,1)*g[1]
sage: x == x_ == x__
True
TESTS::
Expand All @@ -246,7 +269,8 @@ def vector_presentation(self):
base_dict = dict(zip(bas_gen, base_vec.basis()))

# Create a sparse representation of the element.
sparse_coeffs = [x for x in enumerate(self.coefficients()) if not x[1].is_zero()]
sparse_coeffs = [x for x in enumerate(self.dense_coefficient_list())
if not x[1].is_zero()]

vector = base_vec.zero()
for summand_index, algebra_element in sparse_coeffs:
Expand Down
33 changes: 32 additions & 1 deletion src/sage/modules/fp_graded/free_module.py
Original file line number Diff line number Diff line change
Expand Up @@ -429,6 +429,37 @@ def __init__(self, algebra, generator_degrees, category, names=None, **kwds):
Element = FreeGradedModuleElement


def change_ring(self, algebra):
r"""
Change the base ring of ``self``.
INPUT:
- ``algebra`` -- a connected graded algebra
OUTPUT:
The free graded module over ``algebra`` defined with the same
number of generators of the same degrees as ``self``.
EXAMPLES::
sage: from sage.modules.fp_graded.free_module import FreeGradedModule
sage: A = SteenrodAlgebra(2)
sage: A2 = SteenrodAlgebra(2, profile=(3,2,1))
sage: M = FreeGradedModule(A, [0,1])
sage: N = M.change_ring(A2); N
Free graded left module on 2 generators over sub-Hopf algebra of mod 2 Steenrod algebra, milnor basis, profile function [3, 2, 1]
Changing back yields the original module::
sage: N.change_ring(A) is M
True
"""
return FreeGradedModule(algebra, self.generator_degrees())


def _repr_(self):
r"""
Construct a string representation of ``self``.
Expand Down Expand Up @@ -707,7 +738,7 @@ def element_from_coordinates(self, coordinates, n):
# and the total running time of the entire computation dropped from
# 57 to 21 seconds by adding the optimization.
#
element = sum(c * element for c, element in zip(coordinates, basis_elements) if c)
element = self.linear_combination(zip(basis_elements, coordinates))

if not element:
# The previous sum was over the empty list, yielding the integer
Expand Down
6 changes: 2 additions & 4 deletions src/sage/modules/fp_graded/free_morphism.py
Original file line number Diff line number Diff line change
Expand Up @@ -175,10 +175,8 @@ def __call__(self, x):
"""
if x.parent() != self.domain():
raise ValueError('cannot evaluate morphism on element not in the domain')

value = sum((c * v for c, v in zip(x.dense_coefficient_list(), self._values)),
self.codomain().zero())

value = self.codomain().linear_combination(zip(self._values,
x.dense_coefficient_list()))
return value


Expand Down
4 changes: 3 additions & 1 deletion src/sage/modules/fp_graded/homspace.py
Original file line number Diff line number Diff line change
Expand Up @@ -538,7 +538,9 @@ def _trivial_case():
target_degs = [r.degree() + n for r in M.relations()]

block_matrix, R = _create_relations_matrix(
N, [r.coefficients() for r in M.relations()], source_degs, target_degs)
N,
[r.dense_coefficient_list() for r in M.relations()],
source_degs, target_degs)

ker = R.right_kernel()

Expand Down
11 changes: 5 additions & 6 deletions src/sage/modules/fp_graded/module.py
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,7 @@ class FPModule(UniqueRepresentation, IndexedGenerators, Module):
Finitely presented left module on 2 generators and 0 relations over The exterior algebra of rank 2 over Rational Field
"""
@staticmethod
def __classcall_private__(cls, arg0, generator_degrees=None, relations=(), names=None):
def __classcall__(cls, arg0, generator_degrees=None, relations=(), names=None):
r"""
Normalize input to ensure a unique representation.
Expand Down Expand Up @@ -265,9 +265,7 @@ def change_ring(self, algebra):
sage: N.change_ring(A) is M
True
"""
# self.relations() consists of module elements. We need to extra the coefficients.
relations = tuple(r.coefficients() for r in self._j.values())
return FPModule(algebra, self.generator_degrees(), relations)
return FPModule(self._j.change_ring(algebra))


def _from_dict(self, d, coerce=False, remove_zeros=True):
Expand Down Expand Up @@ -748,7 +746,7 @@ def element_from_coordinates(self, coordinates, n):
free_element = self._free_module().element_from_coordinates(
M_n.lift(coordinates), n)

return self(free_element.coefficients())
return self(free_element.dense_coefficient_list())


def __getitem__(self, n):
Expand Down Expand Up @@ -1259,7 +1257,8 @@ def _print_progress(i, k):
# f_1: F_1 -> F_0
_print_progress(1, k)
F_1 = self._j.domain()
pres = Hom(F_1, F_0)(tuple([ F_0(x.coefficients()) for x in self._j.values() ]))
pres = Hom(F_1, F_0)(tuple([ F_0(x.dense_coefficient_list())
for x in self._j.values()]))

ret_complex.append(pres)

Expand Down
26 changes: 16 additions & 10 deletions src/sage/modules/fp_graded/morphism.py
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,8 @@ class FPModuleMorphism(Morphism):
- ``parent`` -- a homspace of finitely presented graded modules
- ``values`` -- a list of elements in the codomain; each element
corresponds to a module generator in the domain
- ``check`` -- boolean (default: ``True``); if ``True``, check
that the morphism is well-defined
TESTS::
Expand Down Expand Up @@ -290,7 +292,8 @@ def change_ring(self, algebra):
# We have to change the ring for the values, too:
new_values = []
for v in self._values:
new_values.append(new_codomain([algebra(a) for a in v.coefficients()]))
new_values.append(new_codomain([algebra(a)
for a in v.dense_coefficient_list()]))
return Hom(self.domain().change_ring(algebra), new_codomain)(new_values)


Expand Down Expand Up @@ -1202,7 +1205,7 @@ def lift(self, f, verbose=False):
for r in L.relations():
target_degree = r.degree() + lift_deg

y = iK.solve(sum([c*x for c,x in zip(r.coefficients(), xs)]))
y = iK.solve(sum([c*x for c,x in zip(r.dense_coefficient_list(), xs)]))
if y is None:
if verbose:
print('The homomorphism cannot be lifted in any '
Expand All @@ -1222,7 +1225,7 @@ def lift(self, f, verbose=False):
return Hom(L, M)(xs)

block_matrix, R = _create_relations_matrix(
K, [r.coefficients() for r in L.relations()], source_degs, target_degs)
K, [r.dense_coefficient_list() for r in L.relations()], source_degs, target_degs)

try:
solution = R.solve_right(vector(ys))
Expand Down Expand Up @@ -1409,7 +1412,8 @@ def suspension(self, t):

D = self.domain().suspension(t)
C = self.codomain().suspension(t)
return Hom(D, C)([C(x.lift_to_free().dense_coefficient_list()) for x in self._values])
return Hom(D, C)([C(x.lift_to_free().dense_coefficient_list())
for x in self._values])


def cokernel_projection(self):
Expand Down Expand Up @@ -1439,8 +1443,9 @@ def cokernel_projection(self):
False
"""
from .module import FPModule
new_relations = [x.coefficients() for x in self.codomain().relations()] +\
[x.coefficients() for x in self._values]
new_relations = ([x.dense_coefficient_list()
for x in self.codomain().relations()] +
[x.dense_coefficient_list() for x in self._values])

coker = FPModule(self.base_ring(),
self.codomain().generator_degrees(),
Expand Down Expand Up @@ -1871,8 +1876,9 @@ def _resolve_image(self, top_dim=None, verbose=False):
print ('The codomain of the morphism is trivial, so there is nothing to resolve.')
return j

self_degree = self.degree()
if self_degree is None:
try:
self_degree = self.degree()
except ValueError:
if verbose:
print ('The homomorphism is trivial, so there is nothing to resolve.')
return j
Expand Down Expand Up @@ -1986,7 +1992,7 @@ def fp_module(self):
from .module import FPModule
return FPModule(self.base_ring(),
self.codomain().generator_degrees(),
tuple([r.coefficients() for r in self._values]))
tuple([r.dense_coefficient_list() for r in self._values]))


def _lift_to_free_morphism(self):
Expand Down Expand Up @@ -2017,7 +2023,7 @@ def _lift_to_free_morphism(self):
raise ValueError("the domain and/or codomain are not free")
M = self.domain()._free_module()
N = self.codomain()._free_module()
return Hom(M, N)([N(v.coefficients()) for v in self.values()])
return Hom(M, N)([N(v.dense_coefficient_list()) for v in self.values()])


@cached_function
Expand Down

0 comments on commit d026799

Please sign in to comment.