]> gitweb.michael.orlitzky.com - sage.d.git/blob - mjo/eja/eja_subalgebra.py
eja: simplify "span of powers" computation in subalgebras.
[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
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):
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 # First compute the vector subspace spanned by the powers of
139 # the given element.
140 powers = [ elt**k for k in range(V.dimension()) ]
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 W = V.span_of_basis( V.from_vector(v) for v in basis_vectors )
154
155 # Now figure out the entries of the right-multiplication
156 # matrix for the successive basis elements b0, b1,... of
157 # that subspace.
158 n = len(superalgebra_basis)
159 mult_table = [[W.zero() for i in range(n)] for j in range(n)]
160 for i in range(n):
161 for j in range(n):
162 product = superalgebra_basis[i]*superalgebra_basis[j]
163 # product.to_vector() might live in a vector subspace
164 # if our parent algebra is already a subalgebra. We
165 # use V.from_vector() to make it "the right size" in
166 # that case.
167 product_vector = V.from_vector(product.to_vector())
168 mult_table[i][j] = W.coordinate_vector(product_vector)
169
170 # The rank is the highest possible degree of a minimal
171 # polynomial, and is bounded above by the dimension. We know
172 # in this case that there's an element whose minimal
173 # polynomial has the same degree as the space's dimension
174 # (remember how we constructed the space?), so that must be
175 # its rank too.
176 rank = W.dimension()
177
178 natural_basis = tuple( b.natural_representation()
179 for b in superalgebra_basis )
180
181
182 self._vector_space = W
183 self._superalgebra_basis = superalgebra_basis
184
185
186 fdeja = super(FiniteDimensionalEuclideanJordanElementSubalgebra, self)
187 return fdeja.__init__(field,
188 mult_table,
189 rank,
190 prefix=prefix,
191 category=category,
192 natural_basis=natural_basis)
193
194
195 def _a_regular_element(self):
196 """
197 Override the superalgebra method to return the one
198 regular element that is sure to exist in this
199 subalgebra, namely the element that generated it.
200
201 SETUP::
202
203 sage: from mjo.eja.eja_algebra import random_eja
204
205 TESTS::
206
207 sage: set_random_seed()
208 sage: J = random_eja().random_element().subalgebra_generated_by()
209 sage: J._a_regular_element().is_regular()
210 True
211
212 """
213 if self.dimension() == 0:
214 return self.zero()
215 else:
216 return self.monomial(1)
217
218
219 def _element_constructor_(self, elt):
220 """
221 Construct an element of this subalgebra from the given one.
222 The only valid arguments are elements of the parent algebra
223 that happen to live in this subalgebra.
224
225 SETUP::
226
227 sage: from mjo.eja.eja_algebra import RealSymmetricEJA
228 sage: from mjo.eja.eja_subalgebra import FiniteDimensionalEuclideanJordanElementSubalgebra
229
230 EXAMPLES::
231
232 sage: J = RealSymmetricEJA(3)
233 sage: x = sum( i*J.gens()[i] for i in range(6) )
234 sage: K = FiniteDimensionalEuclideanJordanElementSubalgebra(x)
235 sage: [ K(x^k) for k in range(J.rank()) ]
236 [f0, f1, f2]
237
238 ::
239
240 """
241 if elt == 0:
242 # Just as in the superalgebra class, we need to hack
243 # this special case to ensure that random_element() can
244 # coerce a ring zero into the algebra.
245 return self.zero()
246
247 if elt in self.superalgebra():
248 coords = self.vector_space().coordinate_vector(elt.to_vector())
249 return self.from_vector(coords)
250
251
252 def one_basis(self):
253 """
254 Return the basis-element-index of this algebra's unit element.
255 """
256 return 0
257
258
259 def one(self):
260 """
261 Return the multiplicative identity element of this algebra.
262
263 The superclass method computes the identity element, which is
264 beyond overkill in this case: the algebra identity should be our
265 first basis element. We implement this via :meth:`one_basis`
266 because that method can optionally be used by other parts of the
267 category framework.
268
269 SETUP::
270
271 sage: from mjo.eja.eja_algebra import (RealCartesianProductEJA,
272 ....: random_eja)
273
274 EXAMPLES::
275
276 sage: J = RealCartesianProductEJA(5)
277 sage: J.one()
278 e0 + e1 + e2 + e3 + e4
279 sage: x = sum(J.gens())
280 sage: A = x.subalgebra_generated_by()
281 sage: A.one()
282 f0
283 sage: A.one().superalgebra_element()
284 e0 + e1 + e2 + e3 + e4
285
286 TESTS:
287
288 The identity element acts like the identity::
289
290 sage: set_random_seed()
291 sage: J = random_eja().random_element().subalgebra_generated_by()
292 sage: x = J.random_element()
293 sage: J.one()*x == x and x*J.one() == x
294 True
295
296 The matrix of the unit element's operator is the identity::
297
298 sage: set_random_seed()
299 sage: J = random_eja().random_element().subalgebra_generated_by()
300 sage: actual = J.one().operator().matrix()
301 sage: expected = matrix.identity(J.base_ring(), J.dimension())
302 sage: actual == expected
303 True
304 """
305 if self.dimension() == 0:
306 return self.zero()
307 else:
308 return self.monomial(self.one_basis())
309
310
311 def natural_basis_space(self):
312 """
313 Return the natural basis space of this algebra, which is identical
314 to that of its superalgebra.
315
316 This is correct "by definition," and avoids a mismatch when the
317 subalgebra is trivial (with no natural basis to infer anything
318 from) and the parent is not.
319 """
320 return self.superalgebra().natural_basis_space()
321
322
323 def superalgebra(self):
324 """
325 Return the superalgebra that this algebra was generated from.
326 """
327 return self._superalgebra
328
329
330 def vector_space(self):
331 """
332 SETUP::
333
334 sage: from mjo.eja.eja_algebra import RealSymmetricEJA
335 sage: from mjo.eja.eja_subalgebra import FiniteDimensionalEuclideanJordanElementSubalgebra
336
337 EXAMPLES::
338
339 sage: J = RealSymmetricEJA(3)
340 sage: x = J.monomial(0) + 2*J.monomial(2) + 5*J.monomial(5)
341 sage: K = FiniteDimensionalEuclideanJordanElementSubalgebra(x)
342 sage: K.vector_space()
343 Vector space of degree 6 and dimension 3 over...
344 User basis matrix:
345 [ 1 0 1 0 0 1]
346 [ 1 0 2 0 0 5]
347 [ 1 0 4 0 0 25]
348 sage: (x^0).to_vector()
349 (1, 0, 1, 0, 0, 1)
350 sage: (x^1).to_vector()
351 (1, 0, 2, 0, 0, 5)
352 sage: (x^2).to_vector()
353 (1, 0, 4, 0, 0, 25)
354
355 """
356 return self._vector_space
357
358
359 Element = FiniteDimensionalEuclideanJordanElementSubalgebraElement