]>
gitweb.michael.orlitzky.com - sage.d.git/blob - octonions.py
289c46a69b075f22409b69f827d7c8efbfee375d
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 MatrixAlgebra
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(MatrixAlgebra
):
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 (i.e. very NOT the octonions).
361 sage: from mjo.octonions import Octonions, 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: O = Octonions(QQ)
375 sage: e0,e1,e2,e3,e4,e5,e6,e7 = O.gens()
376 sage: MS = OctonionMatrixAlgebra(2)
377 sage: MS([ [e0+e4, e1+e5],
378 ....: [e2-e6, e3-e7] ])
379 +---------+---------+
380 | e0 + e4 | e1 + e5 |
381 +---------+---------+
382 | e2 - e6 | e3 - e7 |
383 +---------+---------+
387 sage: set_random_seed()
388 sage: MS = OctonionMatrixAlgebra(ZZ.random_element(10))
389 sage: x = MS.random_element()
390 sage: x*MS.one() == x and MS.one()*x == x
394 def __init__(self
, n
, scalars
=AA
, prefix
="E", **kwargs
):
395 super().__init
__(Octonions(field
=scalars
),