]> gitweb.michael.orlitzky.com - sage.d.git/blob - mjo/eja/euclidean_jordan_algebra.py
097233fdad86dea01e11483d7b7525f0ab816f2a
[sage.d.git] / mjo / eja / euclidean_jordan_algebra.py
1 """
2 Euclidean Jordan Algebras. These are formally-real Jordan Algebras;
3 specifically those where u^2 + v^2 = 0 implies that u = v = 0. They
4 are used in optimization, and have some additional nice methods beyond
5 what can be supported in a general Jordan Algebra.
6 """
7
8 from sage.algebras.finite_dimensional_algebras.finite_dimensional_algebra import FiniteDimensionalAlgebra
9 from sage.algebras.finite_dimensional_algebras.finite_dimensional_algebra_element import FiniteDimensionalAlgebraElement
10
11 class FiniteDimensionalEuclideanJordanAlgebra(FiniteDimensionalAlgebra):
12 @staticmethod
13 def __classcall__(cls, field, mult_table, names='e', category=None):
14 fda = super(FiniteDimensionalEuclideanJordanAlgebra, cls)
15 return fda.__classcall_private__(cls,
16 field,
17 mult_table,
18 names,
19 category)
20
21 def __init__(self, field, mult_table, names='e', category=None):
22 fda = super(FiniteDimensionalEuclideanJordanAlgebra, self)
23 fda.__init__(field, mult_table, names, category)
24
25
26 def _repr_(self):
27 """
28 Return a string representation of ``self``.
29 """
30 return "Euclidean Jordan algebra of degree {} over {}".format(self.degree(), self.base_ring())
31
32 def rank(self):
33 """
34 Return the rank of this EJA.
35 """
36 raise NotImplementedError
37
38
39 class Element(FiniteDimensionalAlgebraElement):
40 """
41 An element of a Euclidean Jordan algebra.
42
43 Since EJAs are commutative, the "right multiplication" matrix is
44 also the left multiplication matrix and must be symmetric::
45
46 sage: set_random_seed()
47 sage: J = eja_ln(5)
48 sage: J.random_element().matrix().is_symmetric()
49 True
50
51 """
52
53 def __pow__(self, n):
54 """
55 Return ``self`` raised to the power ``n``.
56
57 Jordan algebras are always power-associative; see for
58 example Faraut and Koranyi, Proposition II.1.2 (ii).
59 """
60 A = self.parent()
61 if n == 0:
62 return A.one()
63 elif n == 1:
64 return self
65 else:
66 return A.element_class(A, self.vector()*(self.matrix()**(n-1)))
67
68
69 def span_of_powers(self):
70 """
71 Return the vector space spanned by successive powers of
72 this element.
73 """
74 # The dimension of the subalgebra can't be greater than
75 # the big algebra, so just put everything into a list
76 # and let span() get rid of the excess.
77 V = self.vector().parent()
78 return V.span( (self**d).vector() for d in xrange(V.dimension()) )
79
80
81 def degree(self):
82 """
83 Compute the degree of this element the straightforward way
84 according to the definition; by appending powers to a list
85 and figuring out its dimension (that is, whether or not
86 they're linearly dependent).
87
88 EXAMPLES::
89
90 sage: J = eja_ln(4)
91 sage: J.one().degree()
92 1
93 sage: e0,e1,e2,e3 = J.gens()
94 sage: (e0 - e1).degree()
95 2
96
97 In the spin factor algebra (of rank two), all elements that
98 aren't multiples of the identity are regular::
99
100 sage: set_random_seed()
101 sage: n = ZZ.random_element(1,10).abs()
102 sage: J = eja_ln(n)
103 sage: x = J.random_element()
104 sage: x == x.coefficient(0)*J.one() or x.degree() == 2
105 True
106
107 """
108 return self.span_of_powers().dimension()
109
110
111 def subalgebra_generated_by(self):
112 """
113 Return the subalgebra of the parent EJA generated by this element.
114 """
115 # First get the subspace spanned by the powers of myself...
116 V = self.span_of_powers()
117 F = self.base_ring()
118
119 # Now figure out the entries of the right-multiplication
120 # matrix for the successive basis elements b0, b1,... of
121 # that subspace.
122 mats = []
123 for b_right in V.basis():
124 eja_b_right = self.parent()(b_right)
125 b_right_rows = []
126 # The first row of the right-multiplication matrix by
127 # b1 is what we get if we apply that matrix to b1. The
128 # second row of the right multiplication matrix by b1
129 # is what we get when we apply that matrix to b2...
130 for b_left in V.basis():
131 eja_b_left = self.parent()(b_left)
132 # Multiply in the original EJA, but then get the
133 # coordinates from the subalgebra in terms of its
134 # basis.
135 this_row = V.coordinates((eja_b_left*eja_b_right).vector())
136 b_right_rows.append(this_row)
137 b_right_matrix = matrix(F, b_right_rows)
138 mats.append(b_right_matrix)
139
140 return FiniteDimensionalEuclideanJordanAlgebra(F, mats)
141
142
143 def minimal_polynomial(self):
144 return self.matrix().minimal_polynomial()
145
146 def characteristic_polynomial(self):
147 return self.matrix().characteristic_polynomial()
148
149
150 def eja_rn(dimension, field=QQ):
151 """
152 Return the Euclidean Jordan Algebra corresponding to the set
153 `R^n` under the Hadamard product.
154
155 EXAMPLES:
156
157 This multiplication table can be verified by hand::
158
159 sage: J = eja_rn(3)
160 sage: e0,e1,e2 = J.gens()
161 sage: e0*e0
162 e0
163 sage: e0*e1
164 0
165 sage: e0*e2
166 0
167 sage: e1*e1
168 e1
169 sage: e1*e2
170 0
171 sage: e2*e2
172 e2
173
174 """
175 # The FiniteDimensionalAlgebra constructor takes a list of
176 # matrices, the ith representing right multiplication by the ith
177 # basis element in the vector space. So if e_1 = (1,0,0), then
178 # right (Hadamard) multiplication of x by e_1 picks out the first
179 # component of x; and likewise for the ith basis element e_i.
180 Qs = [ matrix(field, dimension, dimension, lambda k,j: 1*(k == j == i))
181 for i in xrange(dimension) ]
182
183 return FiniteDimensionalEuclideanJordanAlgebra(field,Qs)
184
185
186 def eja_ln(dimension, field=QQ):
187 """
188 Return the Jordan algebra corresponding to the Lorentz "ice cream"
189 cone of the given ``dimension``.
190
191 EXAMPLES:
192
193 This multiplication table can be verified by hand::
194
195 sage: J = eja_ln(4)
196 sage: e0,e1,e2,e3 = J.gens()
197 sage: e0*e0
198 e0
199 sage: e0*e1
200 e1
201 sage: e0*e2
202 e2
203 sage: e0*e3
204 e3
205 sage: e1*e2
206 0
207 sage: e1*e3
208 0
209 sage: e2*e3
210 0
211
212 In one dimension, this is the reals under multiplication::
213
214 sage: J1 = eja_ln(1)
215 sage: J2 = eja_rn(1)
216 sage: J1 == J2
217 True
218
219 """
220 Qs = []
221 id_matrix = identity_matrix(field,dimension)
222 for i in xrange(dimension):
223 ei = id_matrix.column(i)
224 Qi = zero_matrix(field,dimension)
225 Qi.set_row(0, ei)
226 Qi.set_column(0, ei)
227 Qi += diagonal_matrix(dimension, [ei[0]]*dimension)
228 # The addition of the diagonal matrix adds an extra ei[0] in the
229 # upper-left corner of the matrix.
230 Qi[0,0] = Qi[0,0] * ~field(2)
231 Qs.append(Qi)
232
233 return FiniteDimensionalEuclideanJordanAlgebra(field,Qs)