Skip to content

Commit

Permalink
sagemathgh-37526: Fix monomial_coefficients for submodules of free …
Browse files Browse the repository at this point in the history
…modules from sage.modules

    
<!-- ^ Please provide a concise and informative title. -->
<!-- ^ Don't put issue numbers in the title, do this in the PR
description below. -->
<!-- ^ For example, instead of "Fixes sagemath#12345" use "Introduce new method
to calculate 1 + 2". -->
<!-- v Describe your changes below in detail. -->
<!-- v Why is this change required? What problem does it solve? -->
<!-- v If this PR resolves an open issue, please link to it here. For
example, "Fixes sagemath#12345". -->

- Rebased version of sagemath#34455
- Part of sagemath#30309

Fixes sagemath#34455

### 📝 Checklist

<!-- Put an `x` in all the boxes that apply. -->

- [x] The title is concise and informative.
- [ ] The description explains in detail what this PR is about.
- [x] I have linked a relevant issue or discussion.
- [ ] I have created tests covering the changes.
- [ ] I have updated the documentation accordingly.

### ⌛ Dependencies

<!-- List all open PRs that this PR logically depends on. For example,
-->
<!-- - sagemath#12345: short description why this is a dependency -->
<!-- - sagemath#34567: ... -->
    
URL: sagemath#37526
Reported by: Matthias Köppe
Reviewer(s): Matthias Köppe, Travis Scrimshaw
  • Loading branch information
Release Manager committed Jun 3, 2024
2 parents e5f42fa + 2eb5de8 commit 9037f6b
Show file tree
Hide file tree
Showing 10 changed files with 141 additions and 13 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
4 changes: 2 additions & 2 deletions src/sage/algebras/lie_algebras/affine_lie_algebra.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
6 changes: 3 additions & 3 deletions src/sage/algebras/lie_algebras/free_lie_algebra.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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")
Expand Down Expand Up @@ -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")

Expand Down
1 change: 1 addition & 0 deletions src/sage/categories/algebras_with_basis.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
1 change: 1 addition & 0 deletions src/sage/categories/hopf_algebras_with_basis.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
33 changes: 33 additions & 0 deletions src/sage/categories/modules_with_basis.py
Original file line number Diff line number Diff line change
Expand Up @@ -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``.
Expand Down
10 changes: 6 additions & 4 deletions src/sage/modular/btquotients/pautomorphicform.py
Original file line number Diff line number Diff line change
Expand Up @@ -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"""
Expand Down
48 changes: 44 additions & 4 deletions src/sage/modules/free_module_element.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -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::
Expand Down Expand Up @@ -2280,8 +2324,6 @@ cdef class FreeModuleElement(Vector): # abstract base class
e[i] = c
return e

monomial_coefficients = dict

#############################
# Plotting
#############################
Expand Down Expand Up @@ -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``.
Expand Down
42 changes: 42 additions & 0 deletions src/sage/quivers/algebra.py
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
8 changes: 8 additions & 0 deletions src/sage/quivers/algebra_elements.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down Expand Up @@ -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:
Expand Down Expand Up @@ -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
Expand Down

0 comments on commit 9037f6b

Please sign in to comment.