]>
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 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()
146 Return the inverse of this element if it exists.
150 sage: from mjo.octonions import Octonions
154 sage: O = Octonions()
155 sage: x = sum(O.gens())
156 sage: x*x.inverse() == O.one()
161 sage: O = Octonions()
162 sage: O.one().inverse() == O.one()
167 sage: set_random_seed()
168 sage: O = Octonions()
169 sage: x = O.random_element()
170 sage: x.is_zero() or ( x*x.inverse() == O.one() )
175 raise ValueError("zero is not invertible")
176 return self
.conjugate()/self
._norm
_squared
()
179 def cayley_dickson(self
, Q
=None):
181 Return the Cayley-Dickson representation of this element in terms
182 of the quaternion algebra ``Q``.
184 The Cayley-Dickson representation is an identification of
185 octionions `x` and `y` with pairs of quaternions `(a,b)` and
186 `(c,d)` respectively such that:
188 * `x + y = (a+b, c+d)`
189 * `xy` = (ac - \bar{d}*b, da + b\bar{c})`
192 where `\bar{x}` denotes the conjugate of `x`.
196 sage: from mjo.octonions import Octonions
200 sage: O = Octonions()
201 sage: x = sum(O.gens())
202 sage: x.cayley_dickson()
203 (1 + i + j + k, 1 + i + j + k)
207 Q
= QuaternionAlgebra(self
.base_ring(), -1, -1)
210 a
= (self
.coefficient(0)*Q
.one() +
211 self
.coefficient(1)*i
+
212 self
.coefficient(2)*j
+
213 self
.coefficient(3)*k
)
214 b
= (self
.coefficient(4)*Q
.one() +
215 self
.coefficient(5)*i
+
216 self
.coefficient(6)*j
+
217 self
.coefficient(7)*k
)
219 from sage
.categories
.sets_cat
import cartesian_product
220 P
= cartesian_product([Q
,Q
])
224 class Octonions(CombinatorialFreeModule
):
228 sage: from mjo.octonions import Octonions
233 Octonion algebra with base ring Algebraic Real Field
234 sage: Octonions(field=QQ)
235 Octonion algebra with base ring Rational Field
242 # Not associative, not commutative
243 category
= MagmaticAlgebras(field
).FiniteDimensional()
244 category
= category
.WithBasis().Unital()
246 super().__init
__(field
,
248 element_class
=Octonion
,
253 # The product of each basis element is plus/minus another
254 # basis element that can simply be looked up on
255 # https://en.wikipedia.org/wiki/Octonion
256 e0
, e1
, e2
, e3
, e4
, e5
, e6
, e7
= self
.gens()
257 self
._multiplication
_table
= (
258 (e0
, e1
, e2
, e3
, e4
, e5
, e6
, e7
),
259 (e1
,-e0
, e3
,-e2
, e5
,-e4
,-e7
, e6
),
260 (e2
,-e3
,-e0
, e1
, e6
, e7
,-e4
,-e5
),
261 (e3
, e2
,-e1
,-e0
, e7
,-e6
, e5
,-e4
),
262 (e4
,-e5
,-e6
,-e7
,-e0
, e1
, e2
, e3
),
263 (e5
, e4
,-e7
, e6
,-e1
,-e0
,-e3
, e2
),
264 (e6
, e7
, e4
,-e5
,-e2
, e3
,-e0
,-e1
),
265 (e7
,-e6
, e5
, e4
,-e3
,-e2
, e1
,-e0
),
268 def product_on_basis(self
, i
, j
):
269 return self
._multiplication
_table
[i
][j
]
273 Return the monomial index (basis element) corresponding to the
274 octonion unit element.
278 sage: from mjo.octonions import Octonions
282 This gives the correct unit element::
284 sage: set_random_seed()
285 sage: O = Octonions()
286 sage: x = O.random_element()
287 sage: x*O.one() == x and O.one()*x == x
294 return ("Octonion algebra with base ring %s" % self
.base_ring())
296 def multiplication_table(self
):
298 Return a visual representation of this algebra's multiplication
299 table (on basis elements).
303 sage: from mjo.octonions import Octonions
307 The multiplication table is what Wikipedia says it is::
309 sage: Octonions().multiplication_table()
310 +----++----+-----+-----+-----+-----+-----+-----+-----+
311 | * || e0 | e1 | e2 | e3 | e4 | e5 | e6 | e7 |
312 +====++====+=====+=====+=====+=====+=====+=====+=====+
313 | e0 || e0 | e1 | e2 | e3 | e4 | e5 | e6 | e7 |
314 +----++----+-----+-----+-----+-----+-----+-----+-----+
315 | e1 || e1 | -e0 | e3 | -e2 | e5 | -e4 | -e7 | e6 |
316 +----++----+-----+-----+-----+-----+-----+-----+-----+
317 | e2 || e2 | -e3 | -e0 | e1 | e6 | e7 | -e4 | -e5 |
318 +----++----+-----+-----+-----+-----+-----+-----+-----+
319 | e3 || e3 | e2 | -e1 | -e0 | e7 | -e6 | e5 | -e4 |
320 +----++----+-----+-----+-----+-----+-----+-----+-----+
321 | e4 || e4 | -e5 | -e6 | -e7 | -e0 | e1 | e2 | e3 |
322 +----++----+-----+-----+-----+-----+-----+-----+-----+
323 | e5 || e5 | e4 | -e7 | e6 | -e1 | -e0 | -e3 | e2 |
324 +----++----+-----+-----+-----+-----+-----+-----+-----+
325 | e6 || e6 | e7 | e4 | -e5 | -e2 | e3 | -e0 | -e1 |
326 +----++----+-----+-----+-----+-----+-----+-----+-----+
327 | e7 || e7 | -e6 | e5 | e4 | -e3 | -e2 | e1 | -e0 |
328 +----++----+-----+-----+-----+-----+-----+-----+-----+
332 # Prepend the header row.
333 M
= [["*"] + list(self
.gens())]
335 # And to each subsequent row, prepend an entry that belongs to
336 # the left-side "header column."
337 M
+= [ [self
.monomial(i
)] + [ self
.monomial(i
)*self
.monomial(j
)
341 return table(M
, header_row
=True, header_column
=True, frame
=True)
345 class OctonionMatrixAlgebra(MatrixAlgebra
):
347 The algebra of ``n``-by-``n`` matrices with octonion entries over
348 (a subfield of) the real numbers.
350 The usual matrix spaces in SageMath don't support octonion entries
351 because they assume that the entries of the matrix come from a
352 commutative and associative ring (i.e. very NOT the octonions).
356 sage: from mjo.octonions import Octonions, OctonionMatrixAlgebra
360 sage: OctonionMatrixAlgebra(3)
361 Module of 3 by 3 matrices with entries in Octonion algebra with base
362 ring Algebraic Real Field over the scalar ring Algebraic Real Field
363 sage: OctonionMatrixAlgebra(3,QQ)
364 Module of 3 by 3 matrices with entries in Octonion algebra with base
365 ring Rational Field over the scalar ring Rational Field
369 sage: O = Octonions(QQ)
370 sage: e0,e1,e2,e3,e4,e5,e6,e7 = O.gens()
371 sage: MS = OctonionMatrixAlgebra(2)
372 sage: MS.from_list([ [e0+e4, e1+e5],
373 ....: [e2-e6, e3-e7] ])
374 +---------+---------+
375 | e0 + e4 | e1 + e5 |
376 +---------+---------+
377 | e2 - e6 | e3 - e7 |
378 +---------+---------+
382 sage: set_random_seed()
383 sage: MS = OctonionMatrixAlgebra(ZZ.random_element(10))
384 sage: x = MS.random_element()
385 sage: x*MS.one() == x and MS.one()*x == x
389 def __init__(self
, n
, scalars
=AA
, prefix
="E", **kwargs
):
390 super().__init
__(Octonions(field
=scalars
),