--- /dev/null
+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())