From 5b9f097f810a8dcc453633ccaee4ea3d0f04ed0b Mon Sep 17 00:00:00 2001 From: Michael Orlitzky Date: Fri, 2 Jan 2026 19:51:12 -0500 Subject: [PATCH] clan: eliminate the user basis stuff from the new clan module I think we'll be able to get away with just using the "real" basis elements as keys to the CFM. This makes it easy to reconstruct the "user" coordinates from CFM elements. --- mjo/clan/clan_element.py | 18 +++++ mjo/clan/unital_clan.py | 147 ++++++++++----------------------------- 2 files changed, 56 insertions(+), 109 deletions(-) create mode 100644 mjo/clan/clan_element.py diff --git a/mjo/clan/clan_element.py b/mjo/clan/clan_element.py new file mode 100644 index 0000000..1d358ef --- /dev/null +++ b/mjo/clan/clan_element.py @@ -0,0 +1,18 @@ +from sage.modules.with_basis.indexed_element import IndexedFreeModuleElement + +class ClanElement(IndexedFreeModuleElement): + """ + An element of a Clan. + """ + + def lift(self): + r""" + Lift this element back in to the ambient vector space on + which the clan structure is defined. + + .. NOTE:: + + For submodules of matrix spaces in particular, a second lift + will be needed to get the actual matrix. + """ + return self.parent()._vector_space.sum_of_terms(self.items()) diff --git a/mjo/clan/unital_clan.py b/mjo/clan/unital_clan.py index 082a5da..927dd3b 100644 --- a/mjo/clan/unital_clan.py +++ b/mjo/clan/unital_clan.py @@ -5,115 +5,51 @@ Proper implementation of unital clans (real, finite dimensional 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.misc import _all2list +from mjo.clan.clan_element import ClanElement class UnitalClan(CombinatorialFreeModule): - Element = IndexedFreeModuleElement + Element = ClanElement def __init__(self, - basis, + vector_space, 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. + scalar_field=QQ, + category=None, + prefix=None, + bracket=False): + + self._vector_space = vector_space + basis = vector_space.basis() + n = vector_space.dimension() + + category = MagmaticAlgebras(scalar_field).or_subcategory(category) + category = category.FiniteDimensional().WithBasis().Unital() + + # Compute the multiplication table. + self._mt = { i: { j : clan_product(basis[i], basis[j]) + for j in basis.keys() } + for i in basis.keys() } + + # Use the vector_space basis keys so we can convert + # easily between them. CombinatorialFreeModule.__init__(self, - field, - range(n), - prefix=prefix, + scalar_field, + vector_space.basis().keys(), 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 + prefix=prefix, + bracket=bracket) + + # Now that the CFM has been initialized, we can coerce + # elements into it to update the multiplication table. + for i in basis.keys(): + for j in basis.keys(): + # The basis keys are the same in this CFM as they are + # in the vector space, so we can just sum the terms + # of each entry in the multiplication table. + self._mt[i][j] = self.sum_of_terms(self._mt[i][j].items()) + def _coerce_map_from_base_ring(self): """ @@ -125,6 +61,7 @@ class UnitalClan(CombinatorialFreeModule): """ return None + def product_on_basis(self, i, j): r""" Returns the Jordan product of the `i` and `j`th basis elements. @@ -133,12 +70,4 @@ class UnitalClan(CombinatorialFreeModule): 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()) + return self._mt[i][j] -- 2.51.0