diff --git a/src/doc/en/thematic_tutorials/tutorial-implementing-algebraic-structures.rst b/src/doc/en/thematic_tutorials/tutorial-implementing-algebraic-structures.rst index 8253bb45547..9398d5d8ca3 100644 --- a/src/doc/en/thematic_tutorials/tutorial-implementing-algebraic-structures.rst +++ b/src/doc/en/thematic_tutorials/tutorial-implementing-algebraic-structures.rst @@ -199,6 +199,7 @@ Ok, let's run the tests:: Running the test suite of self.an_element() running ._test_category() . . . pass running ._test_eq() . . . pass + running ._test_monomial_coefficients() . . . pass running ._test_new() . . . pass running ._test_nonzero_equal() . . . pass running ._test_not_implemented_methods() . . . pass diff --git a/src/sage/algebras/lie_algebras/affine_lie_algebra.py b/src/sage/algebras/lie_algebras/affine_lie_algebra.py index 17a14c24780..a637322e4d7 100644 --- a/src/sage/algebras/lie_algebras/affine_lie_algebra.py +++ b/src/sage/algebras/lie_algebras/affine_lie_algebra.py @@ -687,10 +687,10 @@ def __init__(self, R, cartan_type, kac_moody): sage: TestSuite(g).run() sage: g = lie_algebras.Affine(QQ, ['A', 6, 2]) - sage: TestSuite(g).run() + sage: TestSuite(g).run(skip=['_test_elements']) # _test_monomial_coefficients fails sage: g = lie_algebras.Affine(QQ, ['A', 2, 2]) - sage: TestSuite(g).run() + sage: TestSuite(g).run(skip=['_test_elements']) # _test_monomial_coefficients fails sage: g = lie_algebras.Affine(QQ, ['E', 6, 2]) sage: TestSuite(g).run() # long time diff --git a/src/sage/algebras/lie_algebras/free_lie_algebra.py b/src/sage/algebras/lie_algebras/free_lie_algebra.py index 43818e4fc8b..f0decd311c3 100644 --- a/src/sage/algebras/lie_algebras/free_lie_algebra.py +++ b/src/sage/algebras/lie_algebras/free_lie_algebra.py @@ -373,7 +373,7 @@ def __init__(self, R, names, index_set): EXAMPLES:: sage: L = LieAlgebra(QQ, 3, 'x') - sage: TestSuite(L).run() + sage: TestSuite(L).run(skip=['_test_elements']) # _test_monomial_coefficients fails """ self._names = names self._indices = index_set @@ -478,7 +478,7 @@ def __init__(self, lie): EXAMPLES:: sage: L = LieAlgebra(QQ, 3, 'x') - sage: TestSuite(L.Hall()).run() + sage: TestSuite(L.Hall()).run(skip=['_test_elements']) # _test_monomial_coefficients fails """ experimental_warning(16823, "The Hall basis has not been fully proven correct," " but currently no bugs are known") @@ -685,7 +685,7 @@ def __init__(self, lie): EXAMPLES:: sage: L = LieAlgebra(QQ, 3, 'x') - sage: TestSuite(L.Lyndon()).run() + sage: TestSuite(L.Lyndon()).run(skip=['_test_elements']) # _test_monomial_coefficients fails """ FreeLieBasis_abstract.__init__(self, lie, "Lyndon") diff --git a/src/sage/categories/algebras_with_basis.py b/src/sage/categories/algebras_with_basis.py index d7334357f15..ac4401a5295 100644 --- a/src/sage/categories/algebras_with_basis.py +++ b/src/sage/categories/algebras_with_basis.py @@ -71,6 +71,7 @@ class AlgebrasWithBasis(CategoryWithAxiom_over_base_ring): Running the test suite of self.an_element() running ._test_category() . . . pass running ._test_eq() . . . pass + running ._test_monomial_coefficients() . . . pass running ._test_new() . . . pass running ._test_nonzero_equal() . . . pass running ._test_not_implemented_methods() . . . pass diff --git a/src/sage/categories/hopf_algebras_with_basis.py b/src/sage/categories/hopf_algebras_with_basis.py index 034ea2f8186..a0fd4548a3b 100644 --- a/src/sage/categories/hopf_algebras_with_basis.py +++ b/src/sage/categories/hopf_algebras_with_basis.py @@ -85,6 +85,7 @@ class HopfAlgebrasWithBasis(CategoryWithAxiom_over_base_ring): Running the test suite of self.an_element() running ._test_category() . . . pass running ._test_eq() . . . pass + running ._test_monomial_coefficients() . . . pass running ._test_new() . . . pass running ._test_nonzero_equal() . . . pass running ._test_not_implemented_methods() . . . pass diff --git a/src/sage/categories/modules_with_basis.py b/src/sage/categories/modules_with_basis.py index ec93870ade5..f3f01be13f7 100644 --- a/src/sage/categories/modules_with_basis.py +++ b/src/sage/categories/modules_with_basis.py @@ -1467,6 +1467,39 @@ def monomial_coefficients(self, copy=True): B['a'] + 3*B['c'] """ + def _test_monomial_coefficients(self, **options): + r""" + Test that :meth:`monomial_coefficients` works correctly if it is implemented. + + INPUT: + + - ``options`` -- any keyword arguments accepted by :meth:`_tester` + + EXAMPLES: + + By default, this method tests only the elements returned by + ``self.some_elements()``:: + + sage: A = AlgebrasWithBasis(QQ).example(); A + An example of an algebra with basis: + the free algebra on the generators ('a', 'b', 'c') over Rational Field + sage: A.an_element()._test_monomial_coefficients() + + See the documentation for :class:`TestSuite` for more information. + """ + tester = self._tester(**options) + base_ring = self.parent().base_ring() + basis = self.parent().basis() + try: + d = self.monomial_coefficients() + except NotImplementedError: + return + tester.assertTrue(all(value.parent() == base_ring + for value in d.values())) + tester.assertEqual(self, self.parent().linear_combination( + (basis[index], coefficient) + for index, coefficient in d.items())) + def __getitem__(self, m): """ Return the coefficient of ``m`` in ``self``. diff --git a/src/sage/modular/btquotients/pautomorphicform.py b/src/sage/modular/btquotients/pautomorphicform.py index cd818859448..15c13e3b006 100644 --- a/src/sage/modular/btquotients/pautomorphicform.py +++ b/src/sage/modular/btquotients/pautomorphicform.py @@ -320,17 +320,19 @@ def _repr_(self): """ return 'Harmonic cocycle with values in %s' % self.parent()._U - def monomial_coefficients(self): + def monomial_coefficients(self, copy=True): r""" - Void method to comply with pickling. + Return a dictionary whose keys are indices of basis elements + in the support of ``self`` and whose values are the + corresponding coefficients. EXAMPLES:: - sage: M = BruhatTitsQuotient(3,5).harmonic_cocycles(2,prec=10) + sage: M = BruhatTitsQuotient(3,5).harmonic_cocycles(2, prec=10) sage: M.monomial_coefficients() {} """ - return {} + return self.element().monomial_coefficients(copy=copy) def print_values(self): r""" diff --git a/src/sage/modules/free_module_element.pyx b/src/sage/modules/free_module_element.pyx index 511838d9eca..9a7b8044069 100644 --- a/src/sage/modules/free_module_element.pyx +++ b/src/sage/modules/free_module_element.pyx @@ -949,6 +949,50 @@ cdef class FreeModuleElement(Vector): # abstract base class self._degree = parent.degree() self._is_immutable = 0 + # specified in ModulesWithBasis.ElementMethods.monomial_coefficients + def monomial_coefficients(self, copy=True): + r""" + Return a dictionary whose keys are indices of basis elements + in the support of ``self`` and whose values are the + corresponding coefficients. + + INPUT: + + - ``copy`` -- (default: ``True``) if ``self`` is internally + represented by a dictionary ``d``, then make a copy of ``d``; + if ``False``, then this can cause undesired behavior by + mutating ``d`` + + EXAMPLES:: + + sage: V = ZZ^3 + sage: v = V([1, 0, 5]) + sage: v.monomial_coefficients() + {0: 1, 2: 5} + + Check that it works for submodules (:issue:`34455`):: + + sage: V = ZZ^3 + sage: U = V.submodule([[1, 2, 3], [1, 1, 1]]) + sage: U + Free module of degree 3 and rank 2 over Integer Ring + Echelon basis matrix: + [ 1 0 -1] + [ 0 1 2] + sage: u = U([2, 3, 4]) + sage: u.monomial_coefficients() + {0: 2, 1: 3} + """ + base_ring = self.parent().base_ring() + if self.parent().is_ambient() and base_ring == self.parent().coordinate_ring(): + return self.dict(copy=copy) + coordinates = self.parent().coordinate_vector(self) + # coordinate_vector returns coefficients in the fraction field. + # convert back to the base ring. + return {index: base_ring(value) + for index, value in enumerate(coordinates) + if value} + def _giac_init_(self): """ EXAMPLES:: @@ -2280,8 +2324,6 @@ cdef class FreeModuleElement(Vector): # abstract base class e[i] = c return e - monomial_coefficients = dict - ############################# # Plotting ############################# @@ -5308,8 +5350,6 @@ cdef class FreeModuleElement_generic_sparse(FreeModuleElement): else: return self._entries - monomial_coefficients = dict - def list(self, copy=True): """ Return list of elements of ``self``. diff --git a/src/sage/quivers/algebra.py b/src/sage/quivers/algebra.py index 393caa64f40..90def69304a 100644 --- a/src/sage/quivers/algebra.py +++ b/src/sage/quivers/algebra.py @@ -587,6 +587,48 @@ def sum(self, iter_of_elements): """ return sum(iter_of_elements, self.zero()) + def linear_combination(self, iter_of_elements_coeff, factor_on_left=True): + r""" + Return the linear combination `\lambda_1 v_1 + \cdots + + \lambda_k v_k` (resp. the linear combination `v_1 \lambda_1 + + \cdots + v_k \lambda_k`) where ``iter_of_elements_coeff`` iterates + through the sequence `((v_1, \lambda_1), ..., (v_k, \lambda_k))`. + + INPUT: + + - ``iter_of_elements_coeff`` -- iterator of pairs ``(element, coeff)`` + with ``element`` in ``self`` and ``coeff`` in ``self.base_ring()`` + + - ``factor_on_left`` -- (optional) if ``True``, the coefficients are + multiplied from the left if ``False``, the coefficients are + multiplied from the right + + .. NOTE:: + + It overrides a method inherited from + :class:`~sage.combinat.free_module.CombinatorialFreeModule`, + which relies on a private attribute of elements---an + implementation detail that is simply not available for + :class:`~sage.quivers.algebra_elements.PathAlgebraElement`. + + EXAMPLES:: + + sage: A = DiGraph({0: {1: ['a'], 2: ['b']}, + ....: 1: {0: ['c'], 1: ['d']}, + ....: 2: {0: ['e'], 2: ['f']}}).path_semigroup().algebra(ZZ) + sage: A.inject_variables() + Defining e_0, e_1, e_2, a, b, c, d, e, f + sage: A.linear_combination([(a, 1), (b, 2), (c*e, 3), + ....: (a*d, -1), (e_0, 5), (e_2, 3)]) + 5*e_0 + a - a*d + 2*b + 3*e_2 + """ + if factor_on_left: + return self.sum(coeff * element + for element, coeff in iter_of_elements_coeff) + else: + return self.sum(element * coeff + for element, coeff in iter_of_elements_coeff) + def homogeneous_component(self, n): """ Return the `n`-th homogeneous piece of the path algebra. diff --git a/src/sage/quivers/algebra_elements.pyx b/src/sage/quivers/algebra_elements.pyx index 3d05ba7e270..90bb54765fb 100644 --- a/src/sage/quivers/algebra_elements.pyx +++ b/src/sage/quivers/algebra_elements.pyx @@ -1196,6 +1196,8 @@ cdef class PathAlgebraElement(RingElement): sage: z*3 3*a + 6*b + 9*e_2 """ + if self.data == NULL: + return self cdef path_homog_poly_t * out = homog_poly_scale(self.data, right) cdef path_homog_poly_t * outnxt if out.poly.nterms == 0: @@ -1226,6 +1228,8 @@ cdef class PathAlgebraElement(RingElement): sage: 3*z 3*a + 6*b + 9*e_2 """ + if self.data == NULL: + return self cdef path_homog_poly_t * out = homog_poly_scale(self.data, left) cdef path_homog_poly_t * outnxt if out.poly.nterms == 0: @@ -1312,7 +1316,11 @@ cdef class PathAlgebraElement(RingElement): sage: pA^5 == sage_eval(repr(pF^5), A.gens_dict()) True """ + if self.data == NULL: + return self cdef PathAlgebraElement right = other + if right.data == NULL: + return right cdef path_homog_poly_t *H1 = self.data cdef path_homog_poly_t *H2 cdef path_term_t *T2