class ClanElement(IndexedFreeModuleElement):
"""
- An element of a Clan.
+ An element of a :class:`UnitalClan`.
"""
def lift(self):
for (bi, xi) in self.items()
for (bj, yj) in other.items()
)
+
+
+class NormalDecompositionElement(ClanElement):
+ """
+ An element of a :class:`NormalDecomposition`.
+
+ These may have additional methods that require knowledge of the
+ coordinates in a normal decomposition.
+ """
+ def tr(self):
+ r"""
+ The clan trace of this element; the sum of its diagonal
+ coordinates.
+
+ EXAMPLES::
+
+ sage: from mjo.clan.unital_clan import SnClan
+ sage: C = SnClan(4)
+ sage: C.one().tr()
+ 4
+
+ ::
+
+ sage: C = SnClan(3)
+ sage: x = C.an_element(); x
+ 2*B(0, 0) + 2*B(1, 0) + 3*B(1, 1)
+ sage: x.tr()
+ 5
+
+ """
+ r = self.parent().rank()
+ return sum( self.coefficient((i,i)) for i in range(r) )
from sage.modules.with_basis.subquotient import SubmoduleWithBasis
from sage.rings.rational_field import QQ
-from mjo.clan.clan_element import ClanElement
+from mjo.clan.clan_element import (ClanElement,
+ NormalDecompositionElement)
class UnitalClan(CombinatorialFreeModule):
Element = ClanElement
# Use the vector_space basis keys so we can convert
# easily between them.
- CombinatorialFreeModule.__init__(self,
- scalar_field,
- vector_space.basis().keys(),
- category=category,
- prefix=prefix,
- bracket=bracket)
+ super().__init__(scalar_field,
+ vector_space.basis().keys(),
+ category=category,
+ prefix=prefix,
+ bracket=bracket)
# Now that the CFM has been initialized, we can coerce
# elements into it to update the multiplication table.
return self._mt[i][j]
+
+class NormalDecomposition(UnitalClan):
+ r"""
+ A unital clan for which a normal decomposition is available.
+ This is needed to compute the rank of the cone, and the clan trace
+ as defined by Ishi.
+ """
+ Element = NormalDecompositionElement
+
+ def __init__(self,
+ vector_space,
+ clan_product,
+ inner_product,
+ scalar_field=QQ,
+ category=None,
+ prefix=None,
+ bracket=False):
+ # The normal decomposition assumes that we already have a
+ # lower-triangular (Ishi) basis of the form (i,j) or (i,j,k)
+ # -- the latter when the off-diagonals are greater-than-one
+ # dimensional.
+ self._rank = 1 + max( k[0]
+ for k in vector_space.basis().keys()
+ if k[0] == k[1] )
+
+ super().__init__(vector_space,
+ clan_product,
+ inner_product,
+ scalar_field=QQ,
+ category=None,
+ prefix=None,
+ bracket=False)
+
def rank(self) -> int:
r"""
The rank of this clan. Not implemented by default,
because it is only easy to deduce from a normal decomposition.
"""
- raise NotImplementedError
+ return self._rank
-class SnClan(UnitalClan):
+class SnClan(NormalDecomposition):
r"""
The normally-decomposed clan of real symmetric matrices of a
given size with the clan product and lower-triangular basis
EXAMPLES:
+ The rank of this clan is the size of the matrices::
+
+ sage: n = ZZ.random_element(1,5)
+ sage: C = SnClan(n)
+ sage: C.rank() == n
+ True
+
Verifying the axioms::
sage: n = 3
"""
def __init__(self, n, scalar_field=QQ, **kwargs):
- self._rank = n
-
from sage.matrix.matrix_space import MatrixSpace
Mn = MatrixSpace(scalar_field, n)
b = Mn.basis()
super().__init__(Sn, cp, ip, **kwargs)
- def rank(self) -> int:
- return self._rank
-
-
def __repr__(self) -> str:
r"""
The string representation of this clan.