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