Skip to content

Commit

Permalink
sagemathgh-38166: implement morphisms from free algebras
Browse files Browse the repository at this point in the history
    
trying to enhance the morphisms from free monoids and free algebras.

### 📝 Checklist

- [x] The title is concise and informative.
- [x] The description explains in detail what this PR is about.
- [ ] I have linked a relevant issue or discussion.
- [x] I have created tests covering the changes.
- [ ] I have updated the documentation and checked the documentation
preview.
    
URL: sagemath#38166
Reported by: Frédéric Chapoton
Reviewer(s): Travis Scrimshaw
  • Loading branch information
Release Manager committed Jul 20, 2024
2 parents 34d1bd7 + 5faf5a3 commit 2fa55f3
Show file tree
Hide file tree
Showing 4 changed files with 90 additions and 25 deletions.
23 changes: 23 additions & 0 deletions src/sage/algebras/free_algebra.py
Original file line number Diff line number Diff line change
Expand Up @@ -751,6 +751,29 @@ def _coerce_map_from_(self, R):

return self.base_ring().has_coerce_map_from(R)

def _is_valid_homomorphism_(self, other, im_gens, base_map=None):
"""
Check that the number of given images is correct.
EXAMPLES::
sage: ring = algebras.Free(QQ, ['a', 'b'])
sage: a, b = ring.gens()
sage: A = matrix(QQ, 2, 2, [1, 5, 1, 5])
sage: B = matrix(QQ, 2, 2, [1, 4, 9, 2])
sage: C = matrix(QQ, 2, 2, [1, 7, 8, 9])
sage: f = ring.hom([A, B])
sage: f(a*b+1)
[47 14]
[46 15]
sage: ring.hom([A, B, C])
Traceback (most recent call last):
...
ValueError: number of images must equal number of generators
"""
return len(im_gens) == self.__ngens

def gen(self, i):
"""
The ``i``-th generator of the algebra.
Expand Down
26 changes: 26 additions & 0 deletions src/sage/algebras/free_algebra_element.py
Original file line number Diff line number Diff line change
Expand Up @@ -280,6 +280,32 @@ def _acted_upon_(self, scalar, self_on_left=False):
# _lmul_ = _acted_upon_
# _rmul_ = _acted_upon_

def _im_gens_(self, codomain, im_gens, base_map):
"""
Apply a morphism defined by its values on the generators.
EXAMPLES::
sage: ring = algebras.Free(QQ, ['a', 'b'])
sage: a, b = ring.gens()
sage: A = matrix(QQ, 2, 2, [2, 3, 4, 1])
sage: B = matrix(QQ, 2, 2, [1, 7, 7, 1])
sage: f = ring.hom([A, B])
sage: f(a*b+1)
[24 17]
[11 30]
"""
n = self.parent().ngens()
if n == 0:
cf = next(iter(self._monomial_coefficients.values()))
return codomain.coerce(cf)

if base_map is None:
base_map = codomain

return codomain.sum(base_map(c) * m(*im_gens)
for m, c in self._monomial_coefficients.items())

def variables(self):
"""
Return the variables used in ``self``.
Expand Down
64 changes: 40 additions & 24 deletions src/sage/monoids/free_monoid_element.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
from sage.rings.integer import Integer
from sage.structure.element import MonoidElement
from sage.structure.richcmp import richcmp, richcmp_not_equal
from sage.rings.semirings.non_negative_integer_semiring import NN


def is_FreeMonoidElement(x):
Expand Down Expand Up @@ -176,23 +177,44 @@ def __call__(self, *x, **kwds):
"""
EXAMPLES::
sage: M.<x,y> = FreeMonoid(2)
sage: (x*y).substitute(x=1)
y
sage: M.<a> = FreeMonoid(1)
sage: a.substitute(a=5)
5
sage: M.<x,y,z> = FreeMonoid(3)
sage: (x*y).subs(x=1,y=2,z=14)
2
sage: (x*y).subs({x:z,y:z})
z^2
It is still possible to substitute elements
that have no common parent::
sage: M1 = MatrixSpace(ZZ,1,2) # needs sage.modules
sage: M2 = MatrixSpace(ZZ,2,1) # needs sage.modules
sage: (x*y).subs({x: M1([1,2]), y: M2([3,4])}) # needs sage.modules
[11]
TESTS::
sage: M.<x,y> = FreeMonoid(2)
sage: (x*y).substitute(x=1)
y
sage: (x*y)(QQ(4),QQ(5)).parent()
Rational Field
sage: M.<a> = FreeMonoid(1)
sage: a.substitute(a=5)
5
The codomain is by default the first parent::
sage: M.one()(QQ(4),QQ(5)).parent()
Rational Field
unless there is no variable and no substitution::
sage: M = FreeMonoid(0, [])
sage: M.one()().parent()
Free monoid on 0 generators ()
AUTHORS:
Expand All @@ -210,34 +232,28 @@ def __call__(self, *x, **kwds):
if key in gens_dict:
x[gens_dict[key]] = value

if isinstance(x[0], tuple):
if x and isinstance(x[0], tuple):
x = x[0]

if len(x) != self.parent().ngens():
raise ValueError("must specify as many values as generators in parent")

# I don't start with 0, because I don't want to preclude evaluation with
# arbitrary objects (e.g. matrices) because of funny coercion.
one = P.one()
result = None
# if no substitution, do nothing
if not x:
return self

try:
# This will land in the parent of the first element
result = x[0].parent().one()
except (AttributeError, TypeError):
# unless the parent has no unit
result = NN.one()
for var_index, exponent in self._element_list:
# Take further pains to ensure that non-square matrices are not exponentiated.
replacement = x[var_index]
if exponent > 1:
c = replacement ** exponent
result *= replacement ** exponent
elif exponent == 1:
c = replacement
else:
c = one

if result is None:
result = c
else:
result *= c

if result is None:
return one

result *= replacement
return result

def _mul_(self, y):
Expand Down
2 changes: 1 addition & 1 deletion src/sage/structure/factorization.py
Original file line number Diff line number Diff line change
Expand Up @@ -1206,7 +1206,7 @@ def __call__(self, *args, **kwds):
sage: R.<x,y> = FreeAlgebra(QQ, 2)
sage: F = Factorization([(x,3), (y, 2), (x,1)])
sage: F(x=4)
(1) * 4^3 * y^2 * 4
4^3 * y^2 * 4
sage: F.subs({y:2})
x^3 * 2^2 * x
Expand Down

0 comments on commit 2fa55f3

Please sign in to comment.