03 — The algebra¶
The mathematics¶
A finite-dimensional associative unital algebra A over k is fixed, up to isomorphism, by a basis b_0, ..., b_{m-1} together with its structure constants: the coefficients expressing each product b_i * b_j back in the basis, plus the coordinates of the unit 1_A. Every downstream computation — Hochschild homology, Cartan matrices, the bar complex — needs only these numbers. So quiverlab's central object is not a quiver and not a presentation; it is the structure-constant algebra, and everything else is a way of producing one.
How it is represented¶
The class Algebra (core/algebra.py) carries:
domain— the field k (aDomainfrom Chapter 01);dim— the dimension m;T— the multiplication table, described below;unit— a list of m field elements, the coordinates of 1_A;basis_labels— an optional list of human-readable strings, one per basis vector;quiver,relations— kept when the algebra came from a presentation (used by the Cartan machinery), otherwiseNone.
What T[i][j] literally is. T is a list of lists of lists. T[i][j] is the
coordinate vector of the product b_i * b_j — a list of m field elements, the coefficients
c such that b_i * b_j = sum_t c[t] * b_t. It is a list of coefficients, not a dict, and
it always has length m (mostly zeros). So T[i][j][t] is the coefficient of b_t in
b_i * b_j. (Verified by reading build_monomial_algebra and by executing the example
below.)
unit is unit-adapted when b_0 = 1_A exactly — that is, unit[0] equals one() and
every other coordinate is zero. The flag is_unit_adapted records this; the constructor
computes it if you do not supply it. The bar complex (Chapter 04) requires this form,
because it identifies the reduced space A/k.1 with "drop coordinate 0". An algebra not in
this form is silently fixed by unit_adapted() before any Hochschild computation.
How the computation runs¶
Multiplying two elements¶
multiply(u, v) takes two coordinate vectors and returns their product's coordinate
vector, purely from T: for every nonzero u[i] and nonzero v[j] it forms the scalar
c = u[i]*v[j] and adds c * T[i][j][t] into output coordinate t. It skips zero
coordinates for speed but is otherwise the literal bilinear extension of the table.
Building the table from kQ/I¶
build_monomial_algebra(quiver, relations, field) (core/monomial.py) turns a certified
monomial presentation into an Algebra:
- Admissibility. Each relation must have all paths of length >= 2 (the ideal sits
inside the square of the arrow ideal); a length-1 path raises
AdmissibilityError. - Field placement. The field is materialised on
{0, 1}viamake_domain(Chapter 01), and every relation coefficient is checked nonzero in that field — a relation whose coefficient vanishes mod p is refused as "0 = 0". - Basis.
irreducible_paths(Chapter 02) returns the finite list of irreducible words. The basis is the trivial paths("e", v)for each vertex, followed by the path words("p", w);indexis a dict mapping each basis element (those("e", v)/("p", w)tuples) to its position. - The product rule. A local function
prod(x, y)implements multiplication of two basis elements:None(i.e. zero) if their endpoints do not meet; the other factor if one is a trivial path; otherwise the concatenated word — unless that word now contains a forbidden subword, in which case it is againNone. - Filling
T. For every ordered pair(i, j)the code computesprod(b_i, b_j); if it is a basis element,T[i][j]is the indicator vector withone()in that slot, else the zero vector. - The unit.
unitis the sum of the trivial idempotents e_v — so for a one-vertex quiver,unitalready hasone()in slot 0 and the algebra is born unit-adapted.
Why the code insists on unit-adaptation¶
from_structure_constants (the hand-built entry point) runs _validate: it checks the
supplied unit really is a two-sided identity on every basis vector, and that the table is
associative — (b_i b_j) b_k = b_i (b_j b_k) for all i, j, k — raising QuiverlabError
otherwise. quiverlab never guesses a structure constant. To reach unit-adaptation when
1_A is spread across several idempotents (a multi-vertex algebra), unit_adapted()
performs a change of basis: it builds an invertible matrix P whose column 0 is the unit
vector, then change_of_basis(P) recomputes T and unit in the new basis by solving
the linear systems P x = (b_i b_j) exactly (via solve over the domain). The result is
an isomorphic algebra whose basis vector 0 is 1_A.
A worked micro-example — the literal T for k[x]/(x^3)¶
Built over QQ, Q.algebra(relations=["x^3"], field=QQ) gives dim 3, basis labels
["e_1", "x", "x*x"], so b_0 = 1, b_1 = x, b_2 = x^2, and unit = [1, 0, 0]
(already unit-adapted). The actual table (Fractions written as plain integers):
T[0] = [ [1,0,0], [0,1,0], [0,0,1] ] # 1*1=1, 1*x=x, 1*x^2=x^2
T[1] = [ [0,1,0], [0,0,1], [0,0,0] ] # x*1=x, x*x=x^2, x*x^2=0
T[2] = [ [0,0,1], [0,0,0], [0,0,0] ] # x^2*1=x^2, x^2*x=0, x^2*x^2=0
Read one cell: T[1][1] = [0, 0, 1] says x * x = 01 + 0x + 1*x^2 = x^2. And
T[1][2] = [0, 0, 0] says x * x^2 = 0 — because the concatenated word xxx contains the
forbidden relation and collapses to zero. (This table was produced by running the code.)
Where to look in the code¶
| concept | file | function / class |
|---|---|---|
| the structure-constant algebra | core/algebra.py |
Algebra |
the meaning of T[i][j] |
core/algebra.py |
Algebra.__init__, multiply |
| hand-built table + validation | core/algebra.py |
from_structure_constants, _validate |
| unit-adaptation and base change | core/algebra.py |
unit_adapted, change_of_basis |
| kQ/I -> table | core/monomial.py |
build_monomial_algebra |
| the associativity/unit error | errors.py |
QuiverlabError, AdmissibilityError |