This functionality exists in Sage under the name of group algebras.

For example for a cyclic group:

```
sage: G = ZZ.quotient(10)
sage: A = G.algebra(QQ, category=CommutativeAdditiveGroups(), prefix='x'); A
Algebra of Ring of integers modulo 10 over Rational Field
sage: x = A.basis()
sage: x[G(7)] * x[G(6)]
x3
sage: x[G(7) + G(6)]
x3
sage: (x[G(2)] + x[G(9)])^2
2*x1 + x4 + x8
```

The construction is more general and also works for monoids, semigroups and even for sets (without multiplication), for example:

```
sage: Semigroups().example('free').algebra(ZZ)
Algebra of An example of a semigroup: the free semigroup generated by ('a', 'b', 'c', 'd') over Integer Ring
```

It looks like this is not fully implemented for magmas yet though. A place to start working on this would be to implement Magmas.Algebras similar to Semigroups.Algebras, I think.

Since vector spaces can be viewed as commutative additive groups, this algebra construction should also work for vector spaces. An implementation detail makes this tricky though.

```
sage: V = QQ^4
sage: A = V.algebra(ZZ, prefix='x')
sage: x = A.basis()
sage: v = V([1,2,0,3])
sage: v.set_immutable()
sage: x[v]
x[(1, 2, 0, 3)]
sage: x[v] * x[v]
...
TypeError: mutable vectors are unhashable
```

The implementation of the algebra requires its indexing elements to be immutable. The sum of two (immutable) vectors is not usually immutable though, so the last statement in the previous example fails.

There is no easy solution to this, but a workaround is to define a wrapper class of vectors, which makes sure its elements are immutable and that the addition operation preserves this property. Here is a rudimentary implementation (which might lack some features, see sage.categories.examples for more details):

```
class ImmutableVectors(UniqueRepresentation, Parent):
def __init__(self, V):
self._vspace = V
Parent.__init__(self, category=CommutativeAdditiveGroups())
def _repr_(self):
return "Immutable vectors in %s" % self._vspace
def _an_element_(self):
return self(self._vspace.an_element())
def zero(self):
return self(self._vspace.zero())
class ImmutableVectorElement(ElementWrapper):
def __init__(self, parent, value):
super().__init__(parent, value)
value.set_immutable()
def _add_(self, other):
return ImmutableVectors.Element(self.parent(), self.value + other.value)
Element = ImmutableVectorElement
```

Now the wrapped vectors are immutable.

```
sage: V = QQ^4
sage: V_im = ImmutableVectors(V); V_im
Immutable vectors in Vector space of dimension 4 over Rational Field
sage: set_random_seed(1)
sage: v, w = [V_im(V.random_element()) for _ in (1, 2)]; v, w
((-1/12, 0, -3, -2), (2, 0, -3/2, -1))
sage: v.value.is_immutable()
True
```

Some examples of an algebra over these immutable vectors:

```
sage: A = V_im.algebra(ZZ, prefix='x'); A
Algebra of Immutable vectors in Vector space of dimension 4 over Rational Field over Integer Ring
sage: x = A.basis()
sage: A.an_element()
x(1, 0, 0, 0)
sage: A.one()
x(0, 0, 0, 0)
sage: x[v]
x(-1/12, 0, -3, -2)
sage: x[v] == A.monomial(v)
True
sage: x[v] * x[w]
x(23/12, 0, -9/2, -3)
sage: x[v + w] == x[v] * x[w]
True
sage: u = V_im(V([1..4]))
sage: f = x[u] + x[V_im(1/3 * u.value)]; f
x(1, 2, 3, 4) + x(1/3, 2/3, 1, 4/3)
sage: f^2
x(2, 4, 6, 8) + 2*x(4/3, 8/3, 4, 16/3) + x(2/3, 4/3, 2, 8/3)
```

Beware that this construction is very general, but can also be quite slow on large problems.