]> gitweb.michael.orlitzky.com - sage.d.git/blob - mjo/eja/eja_subalgebra.py
eja: add a WIP gram-schmidt for EJA elements.
[sage.d.git] / mjo / eja / eja_subalgebra.py
1 from sage.matrix.constructor import matrix
2
3 from mjo.eja.eja_algebra import FiniteDimensionalEuclideanJordanAlgebra
4 from mjo.eja.eja_element import FiniteDimensionalEuclideanJordanAlgebraElement
5 from mjo.eja.eja_utils import gram_schmidt
6
7 class FiniteDimensionalEuclideanJordanElementSubalgebraElement(FiniteDimensionalEuclideanJordanAlgebraElement):
8 """
9 SETUP::
10
11 sage: from mjo.eja.eja_algebra import random_eja
12
13 TESTS::
14
15 The natural representation of an element in the subalgebra is
16 the same as its natural representation in the superalgebra::
17
18 sage: set_random_seed()
19 sage: A = random_eja().random_element().subalgebra_generated_by()
20 sage: y = A.random_element()
21 sage: actual = y.natural_representation()
22 sage: expected = y.superalgebra_element().natural_representation()
23 sage: actual == expected
24 True
25
26 """
27
28 def superalgebra_element(self):
29 """
30 Return the object in our algebra's superalgebra that corresponds
31 to myself.
32
33 SETUP::
34
35 sage: from mjo.eja.eja_algebra import (RealSymmetricEJA,
36 ....: random_eja)
37
38 EXAMPLES::
39
40 sage: J = RealSymmetricEJA(3)
41 sage: x = sum(J.gens())
42 sage: x
43 e0 + e1 + e2 + e3 + e4 + e5
44 sage: A = x.subalgebra_generated_by()
45 sage: A(x)
46 f1
47 sage: A(x).superalgebra_element()
48 e0 + e1 + e2 + e3 + e4 + e5
49
50 TESTS:
51
52 We can convert back and forth faithfully::
53
54 sage: set_random_seed()
55 sage: J = random_eja()
56 sage: x = J.random_element()
57 sage: A = x.subalgebra_generated_by()
58 sage: A(x).superalgebra_element() == x
59 True
60 sage: y = A.random_element()
61 sage: A(y.superalgebra_element()) == y
62 True
63
64 """
65 return self.parent().superalgebra().linear_combination(
66 zip(self.parent()._superalgebra_basis, self.to_vector()) )
67
68
69
70
71 class FiniteDimensionalEuclideanJordanElementSubalgebra(FiniteDimensionalEuclideanJordanAlgebra):
72 """
73 The subalgebra of an EJA generated by a single element.
74
75 SETUP::
76
77 sage: from mjo.eja.eja_algebra import (ComplexHermitianEJA,
78 ....: JordanSpinEJA)
79
80 TESTS:
81
82 Ensure that our generator names don't conflict with the superalgebra::
83
84 sage: J = JordanSpinEJA(3)
85 sage: J.one().subalgebra_generated_by().gens()
86 (f0,)
87 sage: J = JordanSpinEJA(3, prefix='f')
88 sage: J.one().subalgebra_generated_by().gens()
89 (g0,)
90 sage: J = JordanSpinEJA(3, prefix='b')
91 sage: J.one().subalgebra_generated_by().gens()
92 (c0,)
93
94 Ensure that we can find subalgebras of subalgebras::
95
96 sage: A = ComplexHermitianEJA(3).one().subalgebra_generated_by()
97 sage: B = A.one().subalgebra_generated_by()
98 sage: B.dimension()
99 1
100
101 """
102 def __init__(self, elt, orthonormalize_basis):
103 self._superalgebra = elt.parent()
104 category = self._superalgebra.category().Associative()
105 V = self._superalgebra.vector_space()
106 field = self._superalgebra.base_ring()
107
108 # A half-assed attempt to ensure that we don't collide with
109 # the superalgebra's prefix (ignoring the fact that there
110 # could be super-superelgrbas in scope). If possible, we
111 # try to "increment" the parent algebra's prefix, although
112 # this idea goes out the window fast because some prefixen
113 # are off-limits.
114 prefixen = [ 'f', 'g', 'h', 'a', 'b', 'c', 'd' ]
115 try:
116 prefix = prefixen[prefixen.index(self._superalgebra.prefix()) + 1]
117 except ValueError:
118 prefix = prefixen[0]
119
120 if elt.is_zero():
121 # Short circuit because 0^0 == 1 is going to make us
122 # think we have a one-dimensional algebra otherwise.
123 natural_basis = tuple()
124 mult_table = tuple()
125 rank = 0
126 self._vector_space = V.zero_subspace()
127 self._superalgebra_basis = []
128 fdeja = super(FiniteDimensionalEuclideanJordanElementSubalgebra,
129 self)
130 return fdeja.__init__(field,
131 mult_table,
132 rank,
133 prefix=prefix,
134 category=category,
135 natural_basis=natural_basis)
136
137
138 # This list is guaranteed to contain all independent powers,
139 # because it's the maximal set of powers that could possibly
140 # be independent (by a dimension argument).
141 powers = [ elt**k for k in range(V.dimension()) ]
142
143 if orthonormalize_basis == False:
144 # In this case, we just need to figure out which elements
145 # of the "powers" list are redundant... First compute the
146 # vector subspace spanned by the powers of the given
147 # element.
148 power_vectors = [ p.to_vector() for p in powers ]
149
150 # Figure out which powers form a linearly-independent set.
151 ind_rows = matrix(field, power_vectors).pivot_rows()
152
153 # Pick those out of the list of all powers.
154 superalgebra_basis = tuple(map(powers.__getitem__, ind_rows))
155
156 # If our superalgebra is a subalgebra of something else, then
157 # these vectors won't have the right coordinates for
158 # V.span_of_basis() unless we use V.from_vector() on them.
159 basis_vectors = map(power_vectors.__getitem__, ind_rows)
160 else:
161 # If we're going to orthonormalize the basis anyway, we
162 # might as well just do Gram-Schmidt on the whole list of
163 # powers. The redundant ones will get zero'd out.
164 superalgebra_basis = gram_schmidt(powers)
165 basis_vectors = [ b.to_vector() for b in superalgebra_basis ]
166
167 W = V.span_of_basis( V.from_vector(v) for v in basis_vectors )
168 n = len(superalgebra_basis)
169 mult_table = [[W.zero() for i in range(n)] for j in range(n)]
170 for i in range(n):
171 for j in range(n):
172 product = superalgebra_basis[i]*superalgebra_basis[j]
173 # product.to_vector() might live in a vector subspace
174 # if our parent algebra is already a subalgebra. We
175 # use V.from_vector() to make it "the right size" in
176 # that case.
177 product_vector = V.from_vector(product.to_vector())
178 mult_table[i][j] = W.coordinate_vector(product_vector)
179
180 # The rank is the highest possible degree of a minimal
181 # polynomial, and is bounded above by the dimension. We know
182 # in this case that there's an element whose minimal
183 # polynomial has the same degree as the space's dimension
184 # (remember how we constructed the space?), so that must be
185 # its rank too.
186 rank = W.dimension()
187
188 natural_basis = tuple( b.natural_representation()
189 for b in superalgebra_basis )
190
191
192 self._vector_space = W
193 self._superalgebra_basis = superalgebra_basis
194
195
196 fdeja = super(FiniteDimensionalEuclideanJordanElementSubalgebra, self)
197 return fdeja.__init__(field,
198 mult_table,
199 rank,
200 prefix=prefix,
201 category=category,
202 natural_basis=natural_basis)
203
204
205 def _a_regular_element(self):
206 """
207 Override the superalgebra method to return the one
208 regular element that is sure to exist in this
209 subalgebra, namely the element that generated it.
210
211 SETUP::
212
213 sage: from mjo.eja.eja_algebra import random_eja
214
215 TESTS::
216
217 sage: set_random_seed()
218 sage: J = random_eja().random_element().subalgebra_generated_by()
219 sage: J._a_regular_element().is_regular()
220 True
221
222 """
223 if self.dimension() == 0:
224 return self.zero()
225 else:
226 return self.monomial(1)
227
228
229 def _element_constructor_(self, elt):
230 """
231 Construct an element of this subalgebra from the given one.
232 The only valid arguments are elements of the parent algebra
233 that happen to live in this subalgebra.
234
235 SETUP::
236
237 sage: from mjo.eja.eja_algebra import RealSymmetricEJA
238 sage: from mjo.eja.eja_subalgebra import FiniteDimensionalEuclideanJordanElementSubalgebra
239
240 EXAMPLES::
241
242 sage: J = RealSymmetricEJA(3)
243 sage: x = sum( i*J.gens()[i] for i in range(6) )
244 sage: K = FiniteDimensionalEuclideanJordanElementSubalgebra(x)
245 sage: [ K(x^k) for k in range(J.rank()) ]
246 [f0, f1, f2]
247
248 ::
249
250 """
251 if elt == 0:
252 # Just as in the superalgebra class, we need to hack
253 # this special case to ensure that random_element() can
254 # coerce a ring zero into the algebra.
255 return self.zero()
256
257 if elt in self.superalgebra():
258 coords = self.vector_space().coordinate_vector(elt.to_vector())
259 return self.from_vector(coords)
260
261
262 def one_basis(self):
263 """
264 Return the basis-element-index of this algebra's unit element.
265 """
266 return 0
267
268
269 def one(self):
270 """
271 Return the multiplicative identity element of this algebra.
272
273 The superclass method computes the identity element, which is
274 beyond overkill in this case: the algebra identity should be our
275 first basis element. We implement this via :meth:`one_basis`
276 because that method can optionally be used by other parts of the
277 category framework.
278
279 SETUP::
280
281 sage: from mjo.eja.eja_algebra import (RealCartesianProductEJA,
282 ....: random_eja)
283
284 EXAMPLES::
285
286 sage: J = RealCartesianProductEJA(5)
287 sage: J.one()
288 e0 + e1 + e2 + e3 + e4
289 sage: x = sum(J.gens())
290 sage: A = x.subalgebra_generated_by()
291 sage: A.one()
292 f0
293 sage: A.one().superalgebra_element()
294 e0 + e1 + e2 + e3 + e4
295
296 TESTS:
297
298 The identity element acts like the identity::
299
300 sage: set_random_seed()
301 sage: J = random_eja().random_element().subalgebra_generated_by()
302 sage: x = J.random_element()
303 sage: J.one()*x == x and x*J.one() == x
304 True
305
306 The matrix of the unit element's operator is the identity::
307
308 sage: set_random_seed()
309 sage: J = random_eja().random_element().subalgebra_generated_by()
310 sage: actual = J.one().operator().matrix()
311 sage: expected = matrix.identity(J.base_ring(), J.dimension())
312 sage: actual == expected
313 True
314 """
315 if self.dimension() == 0:
316 return self.zero()
317 else:
318 return self.monomial(self.one_basis())
319
320
321 def natural_basis_space(self):
322 """
323 Return the natural basis space of this algebra, which is identical
324 to that of its superalgebra.
325
326 This is correct "by definition," and avoids a mismatch when the
327 subalgebra is trivial (with no natural basis to infer anything
328 from) and the parent is not.
329 """
330 return self.superalgebra().natural_basis_space()
331
332
333 def superalgebra(self):
334 """
335 Return the superalgebra that this algebra was generated from.
336 """
337 return self._superalgebra
338
339
340 def vector_space(self):
341 """
342 SETUP::
343
344 sage: from mjo.eja.eja_algebra import RealSymmetricEJA
345 sage: from mjo.eja.eja_subalgebra import FiniteDimensionalEuclideanJordanElementSubalgebra
346
347 EXAMPLES::
348
349 sage: J = RealSymmetricEJA(3)
350 sage: x = J.monomial(0) + 2*J.monomial(2) + 5*J.monomial(5)
351 sage: K = FiniteDimensionalEuclideanJordanElementSubalgebra(x)
352 sage: K.vector_space()
353 Vector space of degree 6 and dimension 3 over...
354 User basis matrix:
355 [ 1 0 1 0 0 1]
356 [ 1 0 2 0 0 5]
357 [ 1 0 4 0 0 25]
358 sage: (x^0).to_vector()
359 (1, 0, 1, 0, 0, 1)
360 sage: (x^1).to_vector()
361 (1, 0, 2, 0, 0, 5)
362 sage: (x^2).to_vector()
363 (1, 0, 4, 0, 0, 25)
364
365 """
366 return self._vector_space
367
368
369 Element = FiniteDimensionalEuclideanJordanElementSubalgebraElement