2 Euclidean Jordan Algebras. These are formally-real Jordan Algebras;
3 specifically those where u^2 + v^2 = 0 implies that u = v = 0. They
4 are used in optimization, and have some additional nice methods beyond
5 what can be supported in a general Jordan Algebra.
8 from sage
.categories
.magmatic_algebras
import MagmaticAlgebras
9 from sage
.structure
.element
import is_Matrix
10 from sage
.structure
.category_object
import normalize_names
12 from sage
.algebras
.finite_dimensional_algebras
.finite_dimensional_algebra
import FiniteDimensionalAlgebra
13 from sage
.algebras
.finite_dimensional_algebras
.finite_dimensional_algebra_element
import FiniteDimensionalAlgebraElement
15 class FiniteDimensionalEuclideanJordanAlgebra(FiniteDimensionalAlgebra
):
17 def __classcall_private__(cls
,
21 assume_associative
=False,
25 mult_table
= [b
.base_extend(field
) for b
in mult_table
]
28 if not (is_Matrix(b
) and b
.dimensions() == (n
, n
)):
29 raise ValueError("input is not a multiplication table")
30 mult_table
= tuple(mult_table
)
32 cat
= MagmaticAlgebras(field
).FiniteDimensional().WithBasis()
33 cat
.or_subcategory(category
)
34 if assume_associative
:
35 cat
= cat
.Associative()
37 names
= normalize_names(n
, names
)
39 fda
= super(FiniteDimensionalEuclideanJordanAlgebra
, cls
)
40 return fda
.__classcall
__(cls
,
43 assume_associative
=assume_associative
,
49 def __init__(self
, field
,
52 assume_associative
=False,
56 fda
= super(FiniteDimensionalEuclideanJordanAlgebra
, self
)
65 Return a string representation of ``self``.
67 fmt
= "Euclidean Jordan algebra of degree {} over {}"
68 return fmt
.format(self
.degree(), self
.base_ring())
72 Return the rank of this EJA.
74 if self
._rank
is None:
75 raise ValueError("no rank specified at genesis")
80 class Element(FiniteDimensionalAlgebraElement
):
82 An element of a Euclidean Jordan algebra.
84 Since EJAs are commutative, the "right multiplication" matrix is
85 also the left multiplication matrix and must be symmetric::
87 sage: set_random_seed()
89 sage: J.random_element().matrix().is_symmetric()
96 Return ``self`` raised to the power ``n``.
98 Jordan algebras are always power-associative; see for
99 example Faraut and Koranyi, Proposition II.1.2 (ii).
107 return A
.element_class(A
, self
.vector()*(self
.matrix()**(n
-1)))
110 def span_of_powers(self
):
112 Return the vector space spanned by successive powers of
115 # The dimension of the subalgebra can't be greater than
116 # the big algebra, so just put everything into a list
117 # and let span() get rid of the excess.
118 V
= self
.vector().parent()
119 return V
.span( (self
**d
).vector() for d
in xrange(V
.dimension()) )
124 Compute the degree of this element the straightforward way
125 according to the definition; by appending powers to a list
126 and figuring out its dimension (that is, whether or not
127 they're linearly dependent).
132 sage: J.one().degree()
134 sage: e0,e1,e2,e3 = J.gens()
135 sage: (e0 - e1).degree()
138 In the spin factor algebra (of rank two), all elements that
139 aren't multiples of the identity are regular::
141 sage: set_random_seed()
142 sage: n = ZZ.random_element(1,10).abs()
144 sage: x = J.random_element()
145 sage: x == x.coefficient(0)*J.one() or x.degree() == 2
149 return self
.span_of_powers().dimension()
152 def subalgebra_generated_by(self
):
154 Return the subalgebra of the parent EJA generated by this element.
156 # First get the subspace spanned by the powers of myself...
157 V
= self
.span_of_powers()
160 # Now figure out the entries of the right-multiplication
161 # matrix for the successive basis elements b0, b1,... of
164 for b_right
in V
.basis():
165 eja_b_right
= self
.parent()(b_right
)
167 # The first row of the right-multiplication matrix by
168 # b1 is what we get if we apply that matrix to b1. The
169 # second row of the right multiplication matrix by b1
170 # is what we get when we apply that matrix to b2...
171 for b_left
in V
.basis():
172 eja_b_left
= self
.parent()(b_left
)
173 # Multiply in the original EJA, but then get the
174 # coordinates from the subalgebra in terms of its
176 this_row
= V
.coordinates((eja_b_left
*eja_b_right
).vector())
177 b_right_rows
.append(this_row
)
178 b_right_matrix
= matrix(F
, b_right_rows
)
179 mats
.append(b_right_matrix
)
181 return FiniteDimensionalEuclideanJordanAlgebra(F
, mats
)
184 def minimal_polynomial(self
):
188 sage: set_random_seed()
189 sage: n = ZZ.random_element(1,10).abs()
191 sage: x = J.random_element()
192 sage: x.degree() == x.minimal_polynomial().degree()
197 sage: set_random_seed()
198 sage: n = ZZ.random_element(1,10).abs()
200 sage: x = J.random_element()
201 sage: x.degree() == x.minimal_polynomial().degree()
204 The minimal polynomial and the characteristic polynomial coincide
205 and are known (see Alizadeh, Example 11.11) for all elements of
206 the spin factor algebra that aren't scalar multiples of the
209 sage: set_random_seed()
210 sage: n = ZZ.random_element(2,10).abs()
212 sage: y = J.random_element()
213 sage: while y == y.coefficient(0)*J.one():
214 ....: y = J.random_element()
215 sage: y0 = y.vector()[0]
216 sage: y_bar = y.vector()[1:]
217 sage: actual = y.minimal_polynomial()
218 sage: x = SR.symbol('x', domain='real')
219 sage: expected = x^2 - 2*y0*x + (y0^2 - norm(y_bar)^2)
220 sage: bool(actual == expected)
224 V
= self
.span_of_powers()
225 assoc_subalg
= self
.subalgebra_generated_by()
226 # Mis-design warning: the basis used for span_of_powers()
227 # and subalgebra_generated_by() must be the same, and in
229 subalg_self
= assoc_subalg(V
.coordinates(self
.vector()))
230 return subalg_self
.matrix().minimal_polynomial()
233 def characteristic_polynomial(self
):
234 return self
.matrix().characteristic_polynomial()
237 def eja_rn(dimension
, field
=QQ
):
239 Return the Euclidean Jordan Algebra corresponding to the set
240 `R^n` under the Hadamard product.
244 This multiplication table can be verified by hand::
247 sage: e0,e1,e2 = J.gens()
262 # The FiniteDimensionalAlgebra constructor takes a list of
263 # matrices, the ith representing right multiplication by the ith
264 # basis element in the vector space. So if e_1 = (1,0,0), then
265 # right (Hadamard) multiplication of x by e_1 picks out the first
266 # component of x; and likewise for the ith basis element e_i.
267 Qs
= [ matrix(field
, dimension
, dimension
, lambda k
,j
: 1*(k
== j
== i
))
268 for i
in xrange(dimension
) ]
270 return FiniteDimensionalEuclideanJordanAlgebra(field
,Qs
,rank
=dimension
)
273 def eja_ln(dimension
, field
=QQ
):
275 Return the Jordan algebra corresponding to the Lorentz "ice cream"
276 cone of the given ``dimension``.
280 This multiplication table can be verified by hand::
283 sage: e0,e1,e2,e3 = J.gens()
299 In one dimension, this is the reals under multiplication::
308 id_matrix
= identity_matrix(field
,dimension
)
309 for i
in xrange(dimension
):
310 ei
= id_matrix
.column(i
)
311 Qi
= zero_matrix(field
,dimension
)
314 Qi
+= diagonal_matrix(dimension
, [ei
[0]]*dimension
)
315 # The addition of the diagonal matrix adds an extra ei[0] in the
316 # upper-left corner of the matrix.
317 Qi
[0,0] = Qi
[0,0] * ~
field(2)
320 return FiniteDimensionalEuclideanJordanAlgebra(field
,Qs
,rank
=2)