]> gitweb.michael.orlitzky.com - sage.d.git/blob - mjo/eja/eja_subalgebra.py
eja: drop custom gram_schmidt() routine that isn't noticeably faster.
[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
6 class FiniteDimensionalEuclideanJordanElementSubalgebraElement(FiniteDimensionalEuclideanJordanAlgebraElement):
7 """
8 SETUP::
9
10 sage: from mjo.eja.eja_algebra import random_eja
11
12 TESTS::
13
14 The natural representation of an element in the subalgebra is
15 the same as its natural representation in the superalgebra::
16
17 sage: set_random_seed()
18 sage: A = random_eja().random_element().subalgebra_generated_by()
19 sage: y = A.random_element()
20 sage: actual = y.natural_representation()
21 sage: expected = y.superalgebra_element().natural_representation()
22 sage: actual == expected
23 True
24
25 The left-multiplication-by operator for elements in the subalgebra
26 works like it does in the superalgebra, even if we orthonormalize
27 our basis::
28
29 sage: set_random_seed()
30 sage: x = random_eja(AA).random_element()
31 sage: A = x.subalgebra_generated_by(orthonormalize_basis=True)
32 sage: y = A.random_element()
33 sage: y.operator()(A.one()) == y
34 True
35
36 """
37
38 def superalgebra_element(self):
39 """
40 Return the object in our algebra's superalgebra that corresponds
41 to myself.
42
43 SETUP::
44
45 sage: from mjo.eja.eja_algebra import (RealSymmetricEJA,
46 ....: random_eja)
47
48 EXAMPLES::
49
50 sage: J = RealSymmetricEJA(3)
51 sage: x = sum(J.gens())
52 sage: x
53 e0 + e1 + e2 + e3 + e4 + e5
54 sage: A = x.subalgebra_generated_by()
55 sage: A(x)
56 f1
57 sage: A(x).superalgebra_element()
58 e0 + e1 + e2 + e3 + e4 + e5
59
60 TESTS:
61
62 We can convert back and forth faithfully::
63
64 sage: set_random_seed()
65 sage: J = random_eja()
66 sage: x = J.random_element()
67 sage: A = x.subalgebra_generated_by()
68 sage: A(x).superalgebra_element() == x
69 True
70 sage: y = A.random_element()
71 sage: A(y.superalgebra_element()) == y
72 True
73
74 """
75 return self.parent().superalgebra().linear_combination(
76 zip(self.parent()._superalgebra_basis, self.to_vector()) )
77
78
79
80
81 class FiniteDimensionalEuclideanJordanElementSubalgebra(FiniteDimensionalEuclideanJordanAlgebra):
82 """
83 The subalgebra of an EJA generated by a single element.
84
85 SETUP::
86
87 sage: from mjo.eja.eja_algebra import (ComplexHermitianEJA,
88 ....: JordanSpinEJA)
89
90 TESTS:
91
92 Ensure that our generator names don't conflict with the superalgebra::
93
94 sage: J = JordanSpinEJA(3)
95 sage: J.one().subalgebra_generated_by().gens()
96 (f0,)
97 sage: J = JordanSpinEJA(3, prefix='f')
98 sage: J.one().subalgebra_generated_by().gens()
99 (g0,)
100 sage: J = JordanSpinEJA(3, prefix='b')
101 sage: J.one().subalgebra_generated_by().gens()
102 (c0,)
103
104 Ensure that we can find subalgebras of subalgebras::
105
106 sage: A = ComplexHermitianEJA(3).one().subalgebra_generated_by()
107 sage: B = A.one().subalgebra_generated_by()
108 sage: B.dimension()
109 1
110
111 """
112 def __init__(self, elt, orthonormalize_basis):
113 self._superalgebra = elt.parent()
114 category = self._superalgebra.category().Associative()
115 V = self._superalgebra.vector_space()
116 field = self._superalgebra.base_ring()
117
118 # A half-assed attempt to ensure that we don't collide with
119 # the superalgebra's prefix (ignoring the fact that there
120 # could be super-superelgrbas in scope). If possible, we
121 # try to "increment" the parent algebra's prefix, although
122 # this idea goes out the window fast because some prefixen
123 # are off-limits.
124 prefixen = [ 'f', 'g', 'h', 'a', 'b', 'c', 'd' ]
125 try:
126 prefix = prefixen[prefixen.index(self._superalgebra.prefix()) + 1]
127 except ValueError:
128 prefix = prefixen[0]
129
130 # This list is guaranteed to contain all independent powers,
131 # because it's the maximal set of powers that could possibly
132 # be independent (by a dimension argument).
133 powers = [ elt**k for k in range(V.dimension()) ]
134 power_vectors = [ p.to_vector() for p in powers ]
135 P = matrix(field, power_vectors)
136
137 if orthonormalize_basis == False:
138 # In this case, we just need to figure out which elements
139 # of the "powers" list are redundant... First compute the
140 # vector subspace spanned by the powers of the given
141 # element.
142
143 # Figure out which powers form a linearly-independent set.
144 ind_rows = P.pivot_rows()
145
146 # Pick those out of the list of all powers.
147 superalgebra_basis = tuple(map(powers.__getitem__, ind_rows))
148
149 # If our superalgebra is a subalgebra of something else, then
150 # these vectors won't have the right coordinates for
151 # V.span_of_basis() unless we use V.from_vector() on them.
152 basis_vectors = map(power_vectors.__getitem__, ind_rows)
153 else:
154 # If we're going to orthonormalize the basis anyway, we
155 # might as well just do Gram-Schmidt on the whole list of
156 # powers. The redundant ones will get zero'd out. If this
157 # looks like a roundabout way to orthonormalize, it is.
158 # But converting everything from algebra elements to vectors
159 # to matrices and then back again turns out to be about
160 # as fast as reimplementing our own Gram-Schmidt that
161 # works in an EJA.
162 G,_ = P.gram_schmidt(orthonormal=True)
163 basis_vectors = [ g for g in G.rows() if not g.is_zero() ]
164 superalgebra_basis = [ self._superalgebra.from_vector(b)
165 for b in basis_vectors ]
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,False)
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
263 def one(self):
264 """
265 Return the multiplicative identity element of this algebra.
266
267 The superclass method computes the identity element, which is
268 beyond overkill in this case: the superalgebra identity
269 restricted to this algebra is its identity. Note that we can't
270 count on the first basis element being the identity -- it migth
271 have been scaled if we orthonormalized the basis.
272
273 SETUP::
274
275 sage: from mjo.eja.eja_algebra import (RealCartesianProductEJA,
276 ....: random_eja)
277
278 EXAMPLES::
279
280 sage: J = RealCartesianProductEJA(5)
281 sage: J.one()
282 e0 + e1 + e2 + e3 + e4
283 sage: x = sum(J.gens())
284 sage: A = x.subalgebra_generated_by()
285 sage: A.one()
286 f0
287 sage: A.one().superalgebra_element()
288 e0 + e1 + e2 + e3 + e4
289
290 TESTS:
291
292 The identity element acts like the identity over the rationals::
293
294 sage: set_random_seed()
295 sage: x = random_eja().random_element()
296 sage: A = x.subalgebra_generated_by()
297 sage: x = A.random_element()
298 sage: A.one()*x == x and x*A.one() == x
299 True
300
301 The identity element acts like the identity over the algebraic
302 reals with an orthonormal basis::
303
304 sage: set_random_seed()
305 sage: x = random_eja(AA).random_element()
306 sage: A = x.subalgebra_generated_by(orthonormalize_basis=True)
307 sage: x = A.random_element()
308 sage: A.one()*x == x and x*A.one() == x
309 True
310
311 The matrix of the unit element's operator is the identity over
312 the rationals::
313
314 sage: set_random_seed()
315 sage: x = random_eja().random_element()
316 sage: A = x.subalgebra_generated_by()
317 sage: actual = A.one().operator().matrix()
318 sage: expected = matrix.identity(A.base_ring(), A.dimension())
319 sage: actual == expected
320 True
321
322 The matrix of the unit element's operator is the identity over
323 the algebraic reals with an orthonormal basis::
324
325 sage: set_random_seed()
326 sage: x = random_eja(AA).random_element()
327 sage: A = x.subalgebra_generated_by(orthonormalize_basis=True)
328 sage: actual = A.one().operator().matrix()
329 sage: expected = matrix.identity(A.base_ring(), A.dimension())
330 sage: actual == expected
331 True
332
333 """
334 if self.dimension() == 0:
335 return self.zero()
336 else:
337 sa_one = self.superalgebra().one().to_vector()
338 sa_coords = self.vector_space().coordinate_vector(sa_one)
339 return self.from_vector(sa_coords)
340
341
342 def natural_basis_space(self):
343 """
344 Return the natural basis space of this algebra, which is identical
345 to that of its superalgebra.
346
347 This is correct "by definition," and avoids a mismatch when the
348 subalgebra is trivial (with no natural basis to infer anything
349 from) and the parent is not.
350 """
351 return self.superalgebra().natural_basis_space()
352
353
354 def superalgebra(self):
355 """
356 Return the superalgebra that this algebra was generated from.
357 """
358 return self._superalgebra
359
360
361 def vector_space(self):
362 """
363 SETUP::
364
365 sage: from mjo.eja.eja_algebra import RealSymmetricEJA
366 sage: from mjo.eja.eja_subalgebra import FiniteDimensionalEuclideanJordanElementSubalgebra
367
368 EXAMPLES::
369
370 sage: J = RealSymmetricEJA(3)
371 sage: x = J.monomial(0) + 2*J.monomial(2) + 5*J.monomial(5)
372 sage: K = FiniteDimensionalEuclideanJordanElementSubalgebra(x,False)
373 sage: K.vector_space()
374 Vector space of degree 6 and dimension 3 over...
375 User basis matrix:
376 [ 1 0 1 0 0 1]
377 [ 1 0 2 0 0 5]
378 [ 1 0 4 0 0 25]
379 sage: (x^0).to_vector()
380 (1, 0, 1, 0, 0, 1)
381 sage: (x^1).to_vector()
382 (1, 0, 2, 0, 0, 5)
383 sage: (x^2).to_vector()
384 (1, 0, 4, 0, 0, 25)
385
386 """
387 return self._vector_space
388
389
390 Element = FiniteDimensionalEuclideanJordanElementSubalgebraElement