From: Michael Orlitzky Date: Fri, 2 Jan 2026 16:39:30 +0000 (-0500) Subject: mjo/clan/unital_clan.py: begin clan implementation X-Git-Url: https://gitweb.michael.orlitzky.com/?a=commitdiff_plain;h=f09d70801d72824f0049c1f94001cd840f6c9434;p=sage.d.git mjo/clan/unital_clan.py: begin clan implementation --- diff --git a/mjo/clan/unital_clan.py b/mjo/clan/unital_clan.py new file mode 100644 index 0000000..f4e3f59 --- /dev/null +++ b/mjo/clan/unital_clan.py @@ -0,0 +1,144 @@ +r""" +Proper implementation of unital clans (real, finite dimensional +"compact" left-symmetric algebras with a unit element). +""" + +from sage.categories.magmatic_algebras import MagmaticAlgebras +from sage.combinat.free_module import CombinatorialFreeModule +from sage.modules.with_basis.indexed_element import IndexedFreeModuleElement + +from mjo.eja.eja_utils import _all2list + +class UnitalClan(CombinatorialFreeModule): + Element = IndexedFreeModuleElement + + def __init__(self, + basis, + clan_product, + inner_product, + field=QQ, + prefix='c', + check_axioms=True): + + n = len(basis) + if n == 0: + raise ValueError("not yet") + + associative = None + commutative = None + if n <= 1: + # All zero- and one-dimensional algebras are just the real + # numbers with (some positive multiples of) the usual + # multiplication as its Jordan and inner-product. + associative = True + commutative = True + + if associative is None: + # We should figure it out. As with check_axioms, we have to do + # this without the help of the _jordan_product_is_associative() + # method because we need to know the category before we + # initialize the algebra. + associative = all( clan_product(clan_product(bi,bj),bk) + == + clan_product(bi,clan_product(bj,bk)) + for bi in basis + for bj in basis + for bk in basis ) + + # Compute the multiplication tables and inner-product matrix. + self._inner_product_matrix = matrix.identity(field, n) + clan_mult = [ [ None for j in range(n) ] + for i in range(n) ] + + for i in range(n): + for j in range(n): + clan_mult[i][j] = clan_product(basis[i], basis[j]) + if j <= i+1: + ip = inner_product(basis[i], basis[j]) + self._inner_product_matrix[i,j] = ip + self._inner_product_matrix[j,i] = ip + self._inner_product_matrix._cache = {'hermitian': True} + self._inner_product_matrix.set_immutable() + + if commutative is None: + commutative = all( clan_mult[i][j] == clan_mult[j][i] + for i in range(n) + for j in range(n) ) + + category = MagmaticAlgebras(field).FiniteDimensional() + category = category.WithBasis().Unital() + + if associative: + category = category.Associative() + if commutative: + category = category.Commutative() + + # Call the superclass constructor so that we can use its from_vector() + # method to build our multiplication table. + CombinatorialFreeModule.__init__(self, + field, + range(n), + prefix=prefix, + category=category, + bracket=False) + + # Now we have to compute the multiplication table by turning + # whatever the input basis was into vectors whose coordinates + # we can compute. + self._input_basis = basis + self._input_basis_space = self._input_basis[0].parent() + + # Now create the vector space for the algebra, which will have + # its own set of non-ambient coordinates (in terms of the + # supplied basis). + degree = len(_all2list(basis[0])) + + # Build an ambient space that fits our matrix basis when + # written out as "long vectors." + V = VectorSpace(field, degree) + vector_basis = tuple( V(_all2list(b)) for b in basis ) + + # Save the span of our matrix basis (when written out as long + # vectors) because otherwise we'll have to reconstruct it + # every time we want to coerce a matrix into the algebra. + self._input_basis_span = V.span_of_basis( vector_basis ) + + for i in range(n): + for j in range(n): + # The jordan product returns a matrixy answer, so we + # have to convert it to the algebra coordinates. + elt = clan_mult[i][j] + elt = self._input_basis_span.coordinate_vector(V(_all2list(elt))) + clan_mult[i][j] = self.from_vector(elt) + + self._multiplication_table = clan_mult + if check_axioms: + pass + + def _coerce_map_from_base_ring(self): + """ + Disable the map from the base ring into the algebra. + + Performing a nonsense conversion like this automatically + is counterpedagogical. The fallback is to try the usual + element constructor, which should also fail. + """ + return None + + def product_on_basis(self, i, j): + r""" + Returns the Jordan product of the `i` and `j`th basis elements. + + This completely defines the clan product, and is used directly + by our superclass machinery to implement :meth:`product`. + + """ + return self._multiplication_table[i][j] + + + def inner_product(self, x, y): + """ + The inner product associated with this clan. + """ + B = self._inner_product_matrix + return (B*x.to_vector()).inner_product(y.to_vector())