]>
gitweb.michael.orlitzky.com - sage.d.git/blob - mjo/octonions.py
1 from sage
.combinat
.free_module
import CombinatorialFreeModule
2 from sage
.modules
.with_basis
.indexed_element
import IndexedFreeModuleElement
3 from sage
.categories
.magmatic_algebras
import MagmaticAlgebras
4 from sage
.rings
.all
import AA
, ZZ
5 from sage
.matrix
.matrix_space
import MatrixSpace
6 from sage
.misc
.table
import table
8 class Octonion(IndexedFreeModuleElement
):
13 sage: from mjo.octonions import Octonions
18 sage: x = sum(O.gens())
20 e0 - e1 - e2 - e3 - e4 - e5 - e6 - e7
24 Conjugating twice gets you the original element::
26 sage: set_random_seed()
28 sage: x = O.random_element()
29 sage: x.conjugate().conjugate() == x
33 C
= MatrixSpace(ZZ
,8).diagonal_matrix((1,-1,-1,-1,-1,-1,-1,-1))
34 return self
.parent().from_vector(C
*self
.to_vector())
38 Return the real part of this octonion.
40 The real part of an octonion is its projection onto the span
41 of the first generator. In other words, the "first dimension"
42 is real and the others are imaginary.
46 sage: from mjo.octonions import Octonions
51 sage: x = sum(O.gens())
57 This method is idempotent::
59 sage: set_random_seed()
61 sage: x = O.random_element()
62 sage: x.real().real() == x.real()
66 return (self
+ self
.conjugate())/2
70 Return the imaginary part of this octonion.
72 The imaginary part of an octonion is its projection onto the
73 orthogonal complement of the span of the first generator. In
74 other words, the "first dimension" is real and the others are
79 sage: from mjo.octonions import Octonions
84 sage: x = sum(O.gens())
86 e1 + e2 + e3 + e4 + e5 + e6 + e7
90 This method is idempotent::
92 sage: set_random_seed()
94 sage: x = O.random_element()
95 sage: x.imag().imag() == x.imag()
99 return (self
- self
.conjugate())/2
101 def _norm_squared(self
):
102 return (self
*self
.conjugate()).coefficient(0)
106 Return the norm of this octonion.
110 sage: from mjo.octonions import Octonions
114 sage: O = Octonions()
120 The norm is nonnegative and belongs to the base field::
122 sage: set_random_seed()
123 sage: O = Octonions()
124 sage: n = O.random_element().norm()
125 sage: n >= 0 and n in O.base_ring()
128 The norm is homogeneous::
130 sage: set_random_seed()
131 sage: O = Octonions()
132 sage: x = O.random_element()
133 sage: alpha = O.base_ring().random_element()
134 sage: (alpha*x).norm() == alpha.abs()*x.norm()
138 return self
._norm
_squared
().sqrt()
142 Return the inverse of this element if it exists.
146 sage: from mjo.octonions import Octonions
150 sage: O = Octonions()
151 sage: x = sum(O.gens())
152 sage: x*x.inverse() == O.one()
157 sage: O = Octonions()
158 sage: O.one().inverse() == O.one()
163 sage: set_random_seed()
164 sage: O = Octonions()
165 sage: x = O.random_element()
166 sage: x.is_zero() or ( x*x.inverse() == O.one() )
171 raise ValueError("zero is not invertible")
172 return self
.conjugate()/self
._norm
_squared
()
174 class Octonions(CombinatorialFreeModule
):
178 sage: from mjo.octonions import Octonions
183 Octonion algebra with base ring Algebraic Real Field
184 sage: Octonions(field=QQ)
185 Octonion algebra with base ring Rational Field
192 # Not associative, not commutative
193 category
= MagmaticAlgebras(field
).FiniteDimensional()
194 category
= category
.WithBasis().Unital()
196 super().__init
__(field
,
198 element_class
=Octonion
,
203 # The product of each basis element is plus/minus another
204 # basis element that can simply be looked up on
205 # https://en.wikipedia.org/wiki/Octonion
206 e0
, e1
, e2
, e3
, e4
, e5
, e6
, e7
= self
.gens()
207 self
._multiplication
_table
= (
208 (e0
, e1
, e2
, e3
, e4
, e5
, e6
, e7
),
209 (e1
,-e0
, e3
,-e2
, e5
,-e4
,-e7
, e6
),
210 (e2
,-e3
,-e0
, e1
, e6
, e7
,-e4
,-e5
),
211 (e3
, e2
,-e1
,-e0
, e7
,-e6
, e5
,-e4
),
212 (e4
,-e5
,-e6
,-e7
,-e0
, e1
, e2
, e3
),
213 (e5
, e4
,-e7
, e6
,-e1
,-e0
,-e3
, e2
),
214 (e6
, e7
, e4
,-e5
,-e2
, e3
,-e0
,-e1
),
215 (e7
,-e6
, e5
, e4
,-e3
,-e2
, e1
,-e0
),
218 def product_on_basis(self
, i
, j
):
219 return self
._multiplication
_table
[i
][j
]
223 Return the monomial index (basis element) corresponding to the
224 octonion unit element.
228 sage: from mjo.octonions import Octonions
232 This gives the correct unit element::
234 sage: set_random_seed()
235 sage: O = Octonions()
236 sage: x = O.random_element()
237 sage: x*O.one() == x and O.one()*x == x
244 return ("Octonion algebra with base ring %s" % self
.base_ring())
246 def multiplication_table(self
):
248 Return a visual representation of this algebra's multiplication
249 table (on basis elements).
253 sage: from mjo.octonions import Octonions
257 The multiplication table is what Wikipedia says it is::
259 sage: Octonions().multiplication_table()
260 +----++----+-----+-----+-----+-----+-----+-----+-----+
261 | * || e0 | e1 | e2 | e3 | e4 | e5 | e6 | e7 |
262 +====++====+=====+=====+=====+=====+=====+=====+=====+
263 | e0 || e0 | e1 | e2 | e3 | e4 | e5 | e6 | e7 |
264 +----++----+-----+-----+-----+-----+-----+-----+-----+
265 | e1 || e1 | -e0 | e3 | -e2 | e5 | -e4 | -e7 | e6 |
266 +----++----+-----+-----+-----+-----+-----+-----+-----+
267 | e2 || e2 | -e3 | -e0 | e1 | e6 | e7 | -e4 | -e5 |
268 +----++----+-----+-----+-----+-----+-----+-----+-----+
269 | e3 || e3 | e2 | -e1 | -e0 | e7 | -e6 | e5 | -e4 |
270 +----++----+-----+-----+-----+-----+-----+-----+-----+
271 | e4 || e4 | -e5 | -e6 | -e7 | -e0 | e1 | e2 | e3 |
272 +----++----+-----+-----+-----+-----+-----+-----+-----+
273 | e5 || e5 | e4 | -e7 | e6 | -e1 | -e0 | -e3 | e2 |
274 +----++----+-----+-----+-----+-----+-----+-----+-----+
275 | e6 || e6 | e7 | e4 | -e5 | -e2 | e3 | -e0 | -e1 |
276 +----++----+-----+-----+-----+-----+-----+-----+-----+
277 | e7 || e7 | -e6 | e5 | e4 | -e3 | -e2 | e1 | -e0 |
278 +----++----+-----+-----+-----+-----+-----+-----+-----+
282 # Prepend the header row.
283 M
= [["*"] + list(self
.gens())]
285 # And to each subsequent row, prepend an entry that belongs to
286 # the left-side "header column."
287 M
+= [ [self
.monomial(i
)] + [ self
.monomial(i
)*self
.monomial(j
)
291 return table(M
, header_row
=True, header_column
=True, frame
=True)