]> gitweb.michael.orlitzky.com - sage.d.git/blobdiff - mjo/eja/eja_algebra.py
WIP: switch away from the algebra base class and use CombinatorialFreeModule.
[sage.d.git] / mjo / eja / eja_algebra.py
index 362b03a6695a00061034cb9168ba76021a9e2daf..5e9c07c01be4bb5ac13644c5335a8b45304c6b7e 100644 (file)
@@ -5,9 +5,10 @@ are used in optimization, and have some additional nice methods beyond
 what can be supported in a general Jordan Algebra.
 """
 
-from sage.algebras.finite_dimensional_algebras.finite_dimensional_algebra import FiniteDimensionalAlgebra
+#from sage.algebras.finite_dimensional_algebras.finite_dimensional_algebra import FiniteDimensionalAlgebra
 from sage.algebras.quatalg.quaternion_algebra import QuaternionAlgebra
 from sage.categories.finite_dimensional_algebras_with_basis import FiniteDimensionalAlgebrasWithBasis
+from sage.combinat.free_module import CombinatorialFreeModule
 from sage.matrix.constructor import matrix
 from sage.misc.cachefunc import cached_method
 from sage.misc.prandom import choice
@@ -20,50 +21,14 @@ from sage.structure.element import is_Matrix
 from sage.structure.category_object import normalize_names
 
 from mjo.eja.eja_element import FiniteDimensionalEuclideanJordanAlgebraElement
-from mjo.eja.eja_utils import _vec2mat, _mat2vec
-
-class FiniteDimensionalEuclideanJordanAlgebra(FiniteDimensionalAlgebra):
-    @staticmethod
-    def __classcall_private__(cls,
-                              field,
-                              mult_table,
-                              rank,
-                              names='e',
-                              assume_associative=False,
-                              category=None,
-                              natural_basis=None):
-        n = len(mult_table)
-        mult_table = [b.base_extend(field) for b in mult_table]
-        for b in mult_table:
-            b.set_immutable()
-            if not (is_Matrix(b) and b.dimensions() == (n, n)):
-                raise ValueError("input is not a multiplication table")
-        mult_table = tuple(mult_table)
-
-        cat = FiniteDimensionalAlgebrasWithBasis(field)
-        cat.or_subcategory(category)
-        if assume_associative:
-            cat = cat.Associative()
-
-        names = normalize_names(n, names)
-
-        fda = super(FiniteDimensionalEuclideanJordanAlgebra, cls)
-        return fda.__classcall__(cls,
-                                 field,
-                                 mult_table,
-                                 rank,
-                                 assume_associative=assume_associative,
-                                 names=names,
-                                 category=cat,
-                                 natural_basis=natural_basis)
-
+from mjo.eja.eja_utils import _mat2vec
 
+class FiniteDimensionalEuclideanJordanAlgebra(CombinatorialFreeModule):
     def __init__(self,
                  field,
                  mult_table,
                  rank,
-                 names='e',
-                 assume_associative=False,
+                 prefix='e',
                  category=None,
                  natural_basis=None):
         """
@@ -86,11 +51,14 @@ class FiniteDimensionalEuclideanJordanAlgebra(FiniteDimensionalAlgebra):
         self._rank = rank
         self._natural_basis = natural_basis
         self._multiplication_table = mult_table
+        if category is None:
+            category = FiniteDimensionalAlgebrasWithBasis(field).Unital()
         fda = super(FiniteDimensionalEuclideanJordanAlgebra, self)
         fda.__init__(field,
-                     mult_table,
-                     names=names,
+                     range(len(mult_table)),
+                     prefix=prefix,
                      category=category)
+        self.print_options(bracket='')
 
 
     def _repr_(self):
@@ -111,9 +79,15 @@ class FiniteDimensionalEuclideanJordanAlgebra(FiniteDimensionalAlgebra):
             Euclidean Jordan algebra of degree 3 over Real Double Field
 
         """
+        # TODO: change this to say "dimension" and fix all the tests.
         fmt = "Euclidean Jordan algebra of degree {} over {}"
-        return fmt.format(self.degree(), self.base_ring())
+        return fmt.format(self.dimension(), self.base_ring())
 
+    def product_on_basis(self, i, j):
+        ei = self.basis()[i]
+        ej = self.basis()[j]
+        Lei = self._multiplication_table[i]
+        return self.from_vector(Lei*ej.to_vector())
 
     def _a_regular_element(self):
         """
@@ -153,7 +127,7 @@ class FiniteDimensionalEuclideanJordanAlgebra(FiniteDimensionalAlgebra):
         """
         z = self._a_regular_element()
         V = self.vector_space()
-        V1 = V.span_of_basis( (z**k).vector() for k in range(self.rank()) )
+        V1 = V.span_of_basis( (z**k).to_vector() for k in range(self.rank()) )
         b =  (V1.basis() + V1.complement().basis())
         return V.span_of_basis(b)
 
@@ -205,11 +179,12 @@ class FiniteDimensionalEuclideanJordanAlgebra(FiniteDimensionalAlgebra):
         n = self.dimension()
 
         # Construct a new algebra over a multivariate polynomial ring...
-        names = ['X' + str(i) for i in range(1,n+1)]
+        names = tuple('X' + str(i) for i in range(1,n+1))
         R = PolynomialRing(self.base_ring(), names)
-        J = FiniteDimensionalEuclideanJordanAlgebra(R,
-                                                    self._multiplication_table,
-                                                    r)
+        J = FiniteDimensionalEuclideanJordanAlgebra(
+              R,
+              tuple(self._multiplication_table),
+              r)
 
         idmat = matrix.identity(J.base_ring(), n)
 
@@ -235,13 +210,13 @@ class FiniteDimensionalEuclideanJordanAlgebra(FiniteDimensionalAlgebra):
 
         # Handle the zeroth power separately, because computing
         # the unit element in J is mathematically suspect.
-        x0 = W.coordinates(self.one().vector())
-        l1  = [ matrix.column(x0) ]
-        l1 += [ matrix.column(W.coordinates((x**k).vector()))
+        x0 = W.coordinate_vector(self.one().to_vector())
+        l1  = [ x0.column() ]
+        l1 += [ W.coordinate_vector((x**k).to_vector()).column()
                 for k in range(1,r) ]
         l2 = [idmat.column(k-1).column() for k in range(r+1, n+1)]
         A_of_x = matrix.block(R, 1, n, (l1 + l2))
-        xr = W.coordinates((x**r).vector())
+        xr = W.coordinate_vector((x**r).to_vector())
         return (A_of_x, x, xr, A_of_x.det())
 
 
@@ -271,7 +246,7 @@ class FiniteDimensionalEuclideanJordanAlgebra(FiniteDimensionalAlgebra):
             sage: J = JordanSpinEJA(3)
             sage: p = J.characteristic_polynomial(); p
             X1^2 - X2^2 - X3^2 + (-2*t)*X1 + t^2
-            sage: xvec = J.one().vector()
+            sage: xvec = J.one().to_vector()
             sage: p(*xvec)
             t^2 - 2*t + 1
 
@@ -359,7 +334,7 @@ class FiniteDimensionalEuclideanJordanAlgebra(FiniteDimensionalAlgebra):
 
             sage: J = RealSymmetricEJA(2)
             sage: J.basis()
-            Family (e0, e1, e2)
+            Finite family {0: e0, 1: e1, 2: e2}
             sage: J.natural_basis()
             (
             [1 0]  [0 1]  [0 0]
@@ -370,7 +345,7 @@ class FiniteDimensionalEuclideanJordanAlgebra(FiniteDimensionalAlgebra):
 
             sage: J = JordanSpinEJA(2)
             sage: J.basis()
-            Family (e0, e1)
+            Finite family {0: e0, 1: e1}
             sage: J.natural_basis()
             (
             [1]  [0]
@@ -379,7 +354,7 @@ class FiniteDimensionalEuclideanJordanAlgebra(FiniteDimensionalAlgebra):
 
         """
         if self._natural_basis is None:
-            return tuple( b.vector().column() for b in self.basis() )
+            return tuple( b.to_vector().column() for b in self.basis() )
         else:
             return self._natural_basis
 
@@ -442,7 +417,7 @@ class FiniteDimensionalEuclideanJordanAlgebra(FiniteDimensionalAlgebra):
 
         # Now if there's an identity element in the algebra, this should work.
         coeffs = A.solve_right(b)
-        return self.linear_combination(zip(coeffs,self.gens()))
+        return self.linear_combination(zip(self.gens(), coeffs))
 
 
     def rank(self):
@@ -520,7 +495,7 @@ class FiniteDimensionalEuclideanJordanAlgebra(FiniteDimensionalAlgebra):
             Vector space of dimension 3 over Rational Field
 
         """
-        return self.zero().vector().parent().ambient_vector_space()
+        return self.zero().to_vector().parent().ambient_vector_space()
 
 
     Element = FiniteDimensionalEuclideanJordanAlgebraElement
@@ -559,18 +534,17 @@ class RealCartesianProductEJA(FiniteDimensionalEuclideanJordanAlgebra):
         e2
 
     """
-    @staticmethod
-    def __classcall_private__(cls, n, field=QQ):
-        # The FiniteDimensionalAlgebra constructor takes a list of
-        # matrices, the ith representing right multiplication by the ith
-        # basis element in the vector space. So if e_1 = (1,0,0), then
-        # right (Hadamard) multiplication of x by e_1 picks out the first
+    def __init__(self, n, field=QQ):
+        # The superclass constructor takes a list of matrices, the ith
+        # representing right multiplication by the ith basis element
+        # in the vector space. So if e_1 = (1,0,0), then right
+        # (Hadamard) multiplication of x by e_1 picks out the first
         # component of x; and likewise for the ith basis element e_i.
         Qs = [ matrix(field, n, n, lambda k,j: 1*(k == j == i))
                for i in xrange(n) ]
 
-        fdeja = super(RealCartesianProductEJA, cls)
-        return fdeja.__classcall_private__(cls, field, Qs, rank=n)
+        fdeja = super(RealCartesianProductEJA, self)
+        return fdeja.__init__(field, Qs, rank=n)
 
     def inner_product(self, x, y):
         return _usual_ip(x,y)
@@ -755,30 +729,21 @@ def _multiplication_table_from_matrix_basis(basis):
     dimension = basis[0].nrows()
 
     V = VectorSpace(field, dimension**2)
-    W = V.span( _mat2vec(s) for s in basis )
-
-    # Taking the span above reorders our basis (thanks, jerk!) so we
-    # need to put our "matrix basis" in the same order as the
-    # (reordered) vector basis.
-    S = tuple( _vec2mat(b) for b in W.basis() )
+    W = V.span_of_basis( _mat2vec(s) for s in basis )
 
     Qs = []
-    for s in S:
+    for s in basis:
         # Brute force the multiplication-by-s matrix by looping
         # through all elements of the basis and doing the computation
-        # to find out what the corresponding row should be. BEWARE:
-        # these multiplication tables won't be symmetric! It therefore
-        # becomes REALLY IMPORTANT that the underlying algebra
-        # constructor uses ROW vectors and not COLUMN vectors. That's
-        # why we're computing rows here and not columns.
-        Q_rows = []
-        for t in S:
-            this_row = _mat2vec((s*t + t*s)/2)
-            Q_rows.append(W.coordinates(this_row))
-        Q = matrix(field, W.dimension(), Q_rows)
+        # to find out what the corresponding row should be.
+        Q_cols = []
+        for t in basis:
+            this_col = _mat2vec((s*t + t*s)/2)
+            Q_cols.append(W.coordinates(this_col))
+        Q = matrix.column(field, W.dimension(), Q_cols)
         Qs.append(Q)
 
-    return (Qs, S)
+    return Qs
 
 
 def _embed_complex_matrix(M):
@@ -1009,7 +974,7 @@ def _unembed_quaternion_matrix(M):
 
 # The usual inner product on R^n.
 def _usual_ip(x,y):
-    return x.vector().inner_product(y.vector())
+    return x.to_vector().inner_product(y.to_vector())
 
 # The inner product used for the real symmetric simple EJA.
 # We keep it as a separate function because e.g. the complex
@@ -1068,17 +1033,15 @@ class RealSymmetricEJA(FiniteDimensionalEuclideanJordanAlgebra):
         True
 
     """
-    @staticmethod
-    def __classcall_private__(cls, n, field=QQ):
+    def __init__(self, n, field=QQ):
         S = _real_symmetric_basis(n, field=field)
-        (Qs, T) = _multiplication_table_from_matrix_basis(S)
+        Qs = _multiplication_table_from_matrix_basis(S)
 
-        fdeja = super(RealSymmetricEJA, cls)
-        return fdeja.__classcall_private__(cls,
-                                           field,
-                                           Qs,
-                                           rank=n,
-                                           natural_basis=T)
+        fdeja = super(RealSymmetricEJA, self)
+        return fdeja.__init__(field,
+                              Qs,
+                              rank=n,
+                              natural_basis=S)
 
     def inner_product(self, x, y):
         return _matrix_ip(x,y)
@@ -1122,17 +1085,16 @@ class ComplexHermitianEJA(FiniteDimensionalEuclideanJordanAlgebra):
         True
 
     """
-    @staticmethod
-    def __classcall_private__(cls, n, field=QQ):
+    def __init__(self, n, field=QQ):
         S = _complex_hermitian_basis(n)
-        (Qs, T) = _multiplication_table_from_matrix_basis(S)
+        Qs = _multiplication_table_from_matrix_basis(S)
+
+        fdeja = super(ComplexHermitianEJA, self)
+        return fdeja.__init__(field,
+                              Qs,
+                              rank=n,
+                              natural_basis=S)
 
-        fdeja = super(ComplexHermitianEJA, cls)
-        return fdeja.__classcall_private__(cls,
-                                           field,
-                                           Qs,
-                                           rank=n,
-                                           natural_basis=T)
 
     def inner_product(self, x, y):
         # Since a+bi on the diagonal is represented as
@@ -1183,17 +1145,15 @@ class QuaternionHermitianEJA(FiniteDimensionalEuclideanJordanAlgebra):
         True
 
     """
-    @staticmethod
-    def __classcall_private__(cls, n, field=QQ):
+    def __init__(self, n, field=QQ):
         S = _quaternion_hermitian_basis(n)
-        (Qs, T) = _multiplication_table_from_matrix_basis(S)
+        Qs = _multiplication_table_from_matrix_basis(S)
 
-        fdeja = super(QuaternionHermitianEJA, cls)
-        return fdeja.__classcall_private__(cls,
-                                           field,
-                                           Qs,
-                                           rank=n,
-                                           natural_basis=T)
+        fdeja = super(QuaternionHermitianEJA, self)
+        return fdeja.__init__(field,
+                              Qs,
+                              rank=n,
+                              natural_basis=S)
 
     def inner_product(self, x, y):
         # Since a+bi+cj+dk on the diagonal is represented as
@@ -1241,8 +1201,7 @@ class JordanSpinEJA(FiniteDimensionalEuclideanJordanAlgebra):
         0
 
     """
-    @staticmethod
-    def __classcall_private__(cls, n, field=QQ):
+    def __init__(self, n, field=QQ):
         Qs = []
         id_matrix = matrix.identity(field, n)
         for i in xrange(n):
@@ -1259,8 +1218,8 @@ class JordanSpinEJA(FiniteDimensionalEuclideanJordanAlgebra):
         # The rank of the spin algebra is two, unless we're in a
         # one-dimensional ambient space (because the rank is bounded by
         # the ambient dimension).
-        fdeja = super(JordanSpinEJA, cls)
-        return fdeja.__classcall_private__(cls, field, Qs, rank=min(n,2))
+        fdeja = super(JordanSpinEJA, self)
+        return fdeja.__init__(field, Qs, rank=min(n,2))
 
     def inner_product(self, x, y):
         return _usual_ip(x,y)