1 from sage
.matrix
.constructor
import matrix
3 from mjo
.eja
.eja_algebra
import FiniteDimensionalEuclideanJordanAlgebra
4 from mjo
.eja
.eja_element
import FiniteDimensionalEuclideanJordanAlgebraElement
6 class FiniteDimensionalEuclideanJordanElementSubalgebraElement(FiniteDimensionalEuclideanJordanAlgebraElement
):
10 sage: from mjo.eja.eja_algebra import random_eja
14 The natural representation of an element in the subalgebra is
15 the same as its natural representation in the superalgebra::
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
25 The left-multiplication-by operator for elements in the subalgebra
26 works like it does in the superalgebra, even if we orthonormalize
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
38 def superalgebra_element(self
):
40 Return the object in our algebra's superalgebra that corresponds
45 sage: from mjo.eja.eja_algebra import (RealSymmetricEJA,
50 sage: J = RealSymmetricEJA(3)
51 sage: x = sum(J.gens())
53 e0 + e1 + e2 + e3 + e4 + e5
54 sage: A = x.subalgebra_generated_by()
57 sage: A(x).superalgebra_element()
58 e0 + e1 + e2 + e3 + e4 + e5
62 We can convert back and forth faithfully::
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
70 sage: y = A.random_element()
71 sage: A(y.superalgebra_element()) == y
75 return self
.parent().superalgebra().linear_combination(
76 zip(self
.parent()._superalgebra
_basis
, self
.to_vector()) )
81 class FiniteDimensionalEuclideanJordanElementSubalgebra(FiniteDimensionalEuclideanJordanAlgebra
):
83 The subalgebra of an EJA generated by a single element.
87 sage: from mjo.eja.eja_algebra import (ComplexHermitianEJA,
92 Ensure that our generator names don't conflict with the superalgebra::
94 sage: J = JordanSpinEJA(3)
95 sage: J.one().subalgebra_generated_by().gens()
97 sage: J = JordanSpinEJA(3, prefix='f')
98 sage: J.one().subalgebra_generated_by().gens()
100 sage: J = JordanSpinEJA(3, prefix='b')
101 sage: J.one().subalgebra_generated_by().gens()
104 Ensure that we can find subalgebras of subalgebras::
106 sage: A = ComplexHermitianEJA(3).one().subalgebra_generated_by()
107 sage: B = A.one().subalgebra_generated_by()
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()
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
124 prefixen
= [ 'f', 'g', 'h', 'a', 'b', 'c', 'd' ]
126 prefix
= prefixen
[prefixen
.index(self
._superalgebra
.prefix()) + 1]
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
)
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
143 # Figure out which powers form a linearly-independent set.
144 ind_rows
= P
.pivot_rows()
146 # Pick those out of the list of all powers.
147 superalgebra_basis
= tuple(map(powers
.__getitem
__, ind_rows
))
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
)
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
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
]
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
)]
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
177 product_vector
= V
.from_vector(product
.to_vector())
178 mult_table
[i
][j
] = W
.coordinate_vector(product_vector
)
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
188 natural_basis
= tuple( b
.natural_representation()
189 for b
in superalgebra_basis
)
192 self
._vector
_space
= W
193 self
._superalgebra
_basis
= superalgebra_basis
196 fdeja
= super(FiniteDimensionalEuclideanJordanElementSubalgebra
, self
)
197 return fdeja
.__init
__(field
,
202 natural_basis
=natural_basis
)
205 def _a_regular_element(self
):
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.
213 sage: from mjo.eja.eja_algebra import random_eja
217 sage: set_random_seed()
218 sage: J = random_eja().random_element().subalgebra_generated_by()
219 sage: J._a_regular_element().is_regular()
223 if self
.dimension() == 0:
226 return self
.monomial(1)
229 def _element_constructor_(self
, elt
):
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.
237 sage: from mjo.eja.eja_algebra import RealSymmetricEJA
238 sage: from mjo.eja.eja_subalgebra import FiniteDimensionalEuclideanJordanElementSubalgebra
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()) ]
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.
257 if elt
in self
.superalgebra():
258 coords
= self
.vector_space().coordinate_vector(elt
.to_vector())
259 return self
.from_vector(coords
)
265 Return the multiplicative identity element of this algebra.
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.
275 sage: from mjo.eja.eja_algebra import (RealCartesianProductEJA,
280 sage: J = RealCartesianProductEJA(5)
282 e0 + e1 + e2 + e3 + e4
283 sage: x = sum(J.gens())
284 sage: A = x.subalgebra_generated_by()
287 sage: A.one().superalgebra_element()
288 e0 + e1 + e2 + e3 + e4
292 The identity element acts like the identity over the rationals::
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
301 The identity element acts like the identity over the algebraic
302 reals with an orthonormal basis::
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
311 The matrix of the unit element's operator is the identity over
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
322 The matrix of the unit element's operator is the identity over
323 the algebraic reals with an orthonormal basis::
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
334 if self
.dimension() == 0:
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
)
342 def natural_basis_space(self
):
344 Return the natural basis space of this algebra, which is identical
345 to that of its superalgebra.
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.
351 return self
.superalgebra().natural_basis_space()
354 def superalgebra(self
):
356 Return the superalgebra that this algebra was generated from.
358 return self
._superalgebra
361 def vector_space(self
):
365 sage: from mjo.eja.eja_algebra import RealSymmetricEJA
366 sage: from mjo.eja.eja_subalgebra import FiniteDimensionalEuclideanJordanElementSubalgebra
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...
379 sage: (x^0).to_vector()
381 sage: (x^1).to_vector()
383 sage: (x^2).to_vector()
387 return self
._vector
_space
390 Element
= FiniteDimensionalEuclideanJordanElementSubalgebraElement