]>
gitweb.michael.orlitzky.com - sage.d.git/blob - mjo/octonions.py
1 from sage
.misc
.cachefunc
import cached_method
2 from sage
.algebras
.quatalg
.quaternion_algebra
import QuaternionAlgebra
3 from sage
.combinat
.free_module
import CombinatorialFreeModule
4 from sage
.modules
.with_basis
.indexed_element
import IndexedFreeModuleElement
5 from sage
.categories
.magmatic_algebras
import MagmaticAlgebras
6 from sage
.rings
.all
import AA
, ZZ
7 from sage
.matrix
.matrix_space
import MatrixSpace
8 from sage
.misc
.table
import table
10 from mjo
.matrix_algebra
import HurwitzMatrixAlgebra
12 class Octonion(IndexedFreeModuleElement
):
17 sage: from mjo.octonions import Octonions
22 sage: x = sum(O.gens())
24 e0 - e1 - e2 - e3 - e4 - e5 - e6 - e7
28 Conjugating twice gets you the original element::
30 sage: set_random_seed()
32 sage: x = O.random_element()
33 sage: x.conjugate().conjugate() == x
37 C
= MatrixSpace(ZZ
,8).diagonal_matrix((1,-1,-1,-1,-1,-1,-1,-1))
38 return self
.parent().from_vector(C
*self
.to_vector())
42 Return the real part of this octonion.
44 The real part of an octonion is its projection onto the span
45 of the first generator. In other words, the "first dimension"
46 is real and the others are imaginary.
50 sage: from mjo.octonions import Octonions
55 sage: x = sum(O.gens())
61 This method is idempotent::
63 sage: set_random_seed()
65 sage: x = O.random_element()
66 sage: x.real().real() == x.real()
70 return (self
+ self
.conjugate())/2
74 Return the imaginary part of this octonion.
76 The imaginary part of an octonion is its projection onto the
77 orthogonal complement of the span of the first generator. In
78 other words, the "first dimension" is real and the others are
83 sage: from mjo.octonions import Octonions
88 sage: x = sum(O.gens())
90 e1 + e2 + e3 + e4 + e5 + e6 + e7
94 This method is idempotent::
96 sage: set_random_seed()
98 sage: x = O.random_element()
99 sage: x.imag().imag() == x.imag()
103 return (self
- self
.conjugate())/2
105 def _norm_squared(self
):
106 return (self
*self
.conjugate()).coefficient(0)
110 Return the norm of this octonion.
114 sage: from mjo.octonions import Octonions
118 sage: O = Octonions()
124 The norm is nonnegative and belongs to the base field::
126 sage: set_random_seed()
127 sage: O = Octonions()
128 sage: n = O.random_element().norm()
129 sage: n >= 0 and n in O.base_ring()
132 The norm is homogeneous::
134 sage: set_random_seed()
135 sage: O = Octonions()
136 sage: x = O.random_element()
137 sage: alpha = O.base_ring().random_element()
138 sage: (alpha*x).norm() == alpha.abs()*x.norm()
142 return self
._norm
_squared
().sqrt()
144 # The absolute value notation is typically used for complex numbers...
145 # and norm() isn't supported in AA, so this lets us use abs() in all
146 # of the division algebras we need.
151 Return the inverse of this element if it exists.
155 sage: from mjo.octonions import Octonions
159 sage: O = Octonions()
160 sage: x = sum(O.gens())
161 sage: x*x.inverse() == O.one()
166 sage: O = Octonions()
167 sage: O.one().inverse() == O.one()
172 sage: set_random_seed()
173 sage: O = Octonions()
174 sage: x = O.random_element()
175 sage: x.is_zero() or ( x*x.inverse() == O.one() )
180 raise ValueError("zero is not invertible")
181 return self
.conjugate()/self
._norm
_squared
()
184 def cayley_dickson(self
, Q
=None):
186 Return the Cayley-Dickson representation of this element in terms
187 of the quaternion algebra ``Q``.
189 The Cayley-Dickson representation is an identification of
190 octionions `x` and `y` with pairs of quaternions `(a,b)` and
191 `(c,d)` respectively such that:
193 * `x + y = (a+b, c+d)`
194 * `xy` = (ac - \bar{d}*b, da + b\bar{c})`
197 where `\bar{x}` denotes the conjugate of `x`.
201 sage: from mjo.octonions import Octonions
205 sage: O = Octonions()
206 sage: x = sum(O.gens())
207 sage: x.cayley_dickson()
208 (1 + i + j + k, 1 + i + j + k)
212 Q
= QuaternionAlgebra(self
.base_ring(), -1, -1)
215 a
= (self
.coefficient(0)*Q
.one() +
216 self
.coefficient(1)*i
+
217 self
.coefficient(2)*j
+
218 self
.coefficient(3)*k
)
219 b
= (self
.coefficient(4)*Q
.one() +
220 self
.coefficient(5)*i
+
221 self
.coefficient(6)*j
+
222 self
.coefficient(7)*k
)
224 from sage
.categories
.sets_cat
import cartesian_product
225 P
= cartesian_product([Q
,Q
])
229 class Octonions(CombinatorialFreeModule
):
233 sage: from mjo.octonions import Octonions
238 Octonion algebra with base ring Algebraic Real Field
239 sage: Octonions(field=QQ)
240 Octonion algebra with base ring Rational Field
247 # Not associative, not commutative
248 category
= MagmaticAlgebras(field
).FiniteDimensional()
249 category
= category
.WithBasis().Unital()
251 super().__init
__(field
,
253 element_class
=Octonion
,
258 # The product of each basis element is plus/minus another
259 # basis element that can simply be looked up on
260 # https://en.wikipedia.org/wiki/Octonion
261 e0
, e1
, e2
, e3
, e4
, e5
, e6
, e7
= self
.gens()
262 self
._multiplication
_table
= (
263 (e0
, e1
, e2
, e3
, e4
, e5
, e6
, e7
),
264 (e1
,-e0
, e3
,-e2
, e5
,-e4
,-e7
, e6
),
265 (e2
,-e3
,-e0
, e1
, e6
, e7
,-e4
,-e5
),
266 (e3
, e2
,-e1
,-e0
, e7
,-e6
, e5
,-e4
),
267 (e4
,-e5
,-e6
,-e7
,-e0
, e1
, e2
, e3
),
268 (e5
, e4
,-e7
, e6
,-e1
,-e0
,-e3
, e2
),
269 (e6
, e7
, e4
,-e5
,-e2
, e3
,-e0
,-e1
),
270 (e7
,-e6
, e5
, e4
,-e3
,-e2
, e1
,-e0
),
273 def product_on_basis(self
, i
, j
):
274 return self
._multiplication
_table
[i
][j
]
278 Return the monomial index (basis element) corresponding to the
279 octonion unit element.
283 sage: from mjo.octonions import Octonions
287 This gives the correct unit element::
289 sage: set_random_seed()
290 sage: O = Octonions()
291 sage: x = O.random_element()
292 sage: x*O.one() == x and O.one()*x == x
299 return ("Octonion algebra with base ring %s" % self
.base_ring())
301 def multiplication_table(self
):
303 Return a visual representation of this algebra's multiplication
304 table (on basis elements).
308 sage: from mjo.octonions import Octonions
312 The multiplication table is what Wikipedia says it is::
314 sage: Octonions().multiplication_table()
315 +----++----+-----+-----+-----+-----+-----+-----+-----+
316 | * || e0 | e1 | e2 | e3 | e4 | e5 | e6 | e7 |
317 +====++====+=====+=====+=====+=====+=====+=====+=====+
318 | e0 || e0 | e1 | e2 | e3 | e4 | e5 | e6 | e7 |
319 +----++----+-----+-----+-----+-----+-----+-----+-----+
320 | e1 || e1 | -e0 | e3 | -e2 | e5 | -e4 | -e7 | e6 |
321 +----++----+-----+-----+-----+-----+-----+-----+-----+
322 | e2 || e2 | -e3 | -e0 | e1 | e6 | e7 | -e4 | -e5 |
323 +----++----+-----+-----+-----+-----+-----+-----+-----+
324 | e3 || e3 | e2 | -e1 | -e0 | e7 | -e6 | e5 | -e4 |
325 +----++----+-----+-----+-----+-----+-----+-----+-----+
326 | e4 || e4 | -e5 | -e6 | -e7 | -e0 | e1 | e2 | e3 |
327 +----++----+-----+-----+-----+-----+-----+-----+-----+
328 | e5 || e5 | e4 | -e7 | e6 | -e1 | -e0 | -e3 | e2 |
329 +----++----+-----+-----+-----+-----+-----+-----+-----+
330 | e6 || e6 | e7 | e4 | -e5 | -e2 | e3 | -e0 | -e1 |
331 +----++----+-----+-----+-----+-----+-----+-----+-----+
332 | e7 || e7 | -e6 | e5 | e4 | -e3 | -e2 | e1 | -e0 |
333 +----++----+-----+-----+-----+-----+-----+-----+-----+
337 # Prepend the header row.
338 M
= [["*"] + list(self
.gens())]
340 # And to each subsequent row, prepend an entry that belongs to
341 # the left-side "header column."
342 M
+= [ [self
.monomial(i
)] + [ self
.monomial(i
)*self
.monomial(j
)
346 return table(M
, header_row
=True, header_column
=True, frame
=True)
350 class OctonionMatrixAlgebra(HurwitzMatrixAlgebra
):
352 The algebra of ``n``-by-``n`` matrices with octonion entries over
353 (a subfield of) the real numbers.
355 The usual matrix spaces in SageMath don't support octonion entries
356 because they assume that the entries of the matrix come from a
357 commutative and associative ring, and the octonions are neither.
361 sage: from mjo.octonions import OctonionMatrixAlgebra
365 sage: OctonionMatrixAlgebra(3)
366 Module of 3 by 3 matrices with entries in Octonion algebra with base
367 ring Algebraic Real Field over the scalar ring Algebraic Real Field
368 sage: OctonionMatrixAlgebra(3,QQ)
369 Module of 3 by 3 matrices with entries in Octonion algebra with base
370 ring Rational Field over the scalar ring Rational Field
374 sage: A = OctonionMatrixAlgebra(2)
375 sage: e0,e1,e2,e3,e4,e5,e6,e7 = A.entry_algebra().gens()
376 sage: A([ [e0+e4, e1+e5],
377 ....: [e2-e6, e3-e7] ])
378 +---------+---------+
379 | e0 + e4 | e1 + e5 |
380 +---------+---------+
381 | e2 - e6 | e3 - e7 |
382 +---------+---------+
386 sage: A1 = OctonionMatrixAlgebra(1,QQ)
387 sage: A2 = OctonionMatrixAlgebra(1,QQ)
388 sage: cartesian_product([A1,A2])
389 Module of 1 by 1 matrices with entries in Octonion algebra with
390 base ring Rational Field over the scalar ring Rational Field (+)
391 Module of 1 by 1 matrices with entries in Octonion algebra with
392 base ring Rational Field over the scalar ring Rational Field
396 sage: set_random_seed()
397 sage: A = OctonionMatrixAlgebra(ZZ.random_element(10))
398 sage: x = A.random_element()
399 sage: x*A.one() == x and A.one()*x == x
403 def __init__(self
, n
, scalars
=AA
, prefix
="E", **kwargs
):
404 super().__init
__(Octonions(field
=scalars
),
410 class QuaternionMatrixAlgebra(HurwitzMatrixAlgebra
):
412 The algebra of ``n``-by-``n`` matrices with quaternion entries over
413 (a subfield of) the real numbers.
415 The usual matrix spaces in SageMath don't support quaternion entries
416 because they assume that the entries of the matrix come from a
417 commutative ring, and the quaternions are not commutative.
421 sage: from mjo.octonions import QuaternionMatrixAlgebra
425 sage: QuaternionMatrixAlgebra(3)
426 Module of 3 by 3 matrices with entries in Quaternion
427 Algebra (-1, -1) with base ring Algebraic Real Field
428 over the scalar ring Algebraic Real Field
429 sage: QuaternionMatrixAlgebra(3,QQ)
430 Module of 3 by 3 matrices with entries in Quaternion
431 Algebra (-1, -1) with base ring Rational Field over
432 the scalar ring Rational Field
436 sage: A = QuaternionMatrixAlgebra(2)
437 sage: i,j,k = A.entry_algebra().gens()
438 sage: A([ [1+i, j-2],
448 sage: A1 = QuaternionMatrixAlgebra(1,QQ)
449 sage: A2 = QuaternionMatrixAlgebra(2,QQ)
450 sage: cartesian_product([A1,A2])
451 Module of 1 by 1 matrices with entries in Quaternion Algebra
452 (-1, -1) with base ring Rational Field over the scalar ring
453 Rational Field (+) Module of 2 by 2 matrices with entries in
454 Quaternion Algebra (-1, -1) with base ring Rational Field over
455 the scalar ring Rational Field
459 sage: set_random_seed()
460 sage: A = QuaternionMatrixAlgebra(ZZ.random_element(10))
461 sage: x = A.random_element()
462 sage: x*A.one() == x and A.one()*x == x
466 def __init__(self
, n
, scalars
=AA
, **kwargs
):
467 # The -1,-1 gives us the "usual" definition of quaternion
468 Q
= QuaternionAlgebra(scalars
,-1,-1)
469 super().__init
__(Q
, scalars
, n
, **kwargs
)