diff --git a/src/sage/modules/fp_graded/element.py b/src/sage/modules/fp_graded/element.py index 87f52b759ae..fc9f92a6669 100755 --- a/src/sage/modules/fp_graded/element.py +++ b/src/sage/modules/fp_graded/element.py @@ -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 @@ -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. = 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): @@ -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:: diff --git a/src/sage/modules/fp_graded/free_element.py b/src/sage/modules/fp_graded/free_element.py index 97c0db1978e..b852e089c42 100755 --- a/src/sage/modules/fp_graded/free_element.py +++ b/src/sage/modules/fp_graded/free_element.py @@ -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""" @@ -49,10 +50,18 @@ 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 @@ -60,10 +69,21 @@ def coefficients(self): sage: M. = 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. = 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): @@ -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: @@ -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): @@ -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:: @@ -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: diff --git a/src/sage/modules/fp_graded/free_module.py b/src/sage/modules/fp_graded/free_module.py index b867fcb28fa..7c5bbaf83cc 100755 --- a/src/sage/modules/fp_graded/free_module.py +++ b/src/sage/modules/fp_graded/free_module.py @@ -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``. @@ -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 diff --git a/src/sage/modules/fp_graded/free_morphism.py b/src/sage/modules/fp_graded/free_morphism.py index ff4d8453053..a4d50e13a65 100755 --- a/src/sage/modules/fp_graded/free_morphism.py +++ b/src/sage/modules/fp_graded/free_morphism.py @@ -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 diff --git a/src/sage/modules/fp_graded/homspace.py b/src/sage/modules/fp_graded/homspace.py index 19436bbac3c..ef90eb70040 100755 --- a/src/sage/modules/fp_graded/homspace.py +++ b/src/sage/modules/fp_graded/homspace.py @@ -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() diff --git a/src/sage/modules/fp_graded/module.py b/src/sage/modules/fp_graded/module.py index 14818f8ffac..efca02b8335 100755 --- a/src/sage/modules/fp_graded/module.py +++ b/src/sage/modules/fp_graded/module.py @@ -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. @@ -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): @@ -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): @@ -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) diff --git a/src/sage/modules/fp_graded/morphism.py b/src/sage/modules/fp_graded/morphism.py index 9a6e2ff4139..7b46b1b0bb4 100755 --- a/src/sage/modules/fp_graded/morphism.py +++ b/src/sage/modules/fp_graded/morphism.py @@ -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:: @@ -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) @@ -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 ' @@ -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)) @@ -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): @@ -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(), @@ -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 @@ -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): @@ -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