X-Git-Url: http://gitweb.michael.orlitzky.com/?a=blobdiff_plain;f=mjo%2Foctonions.py;fp=mjo%2Foctonions.py;h=0000000000000000000000000000000000000000;hb=e28bd3518185e3a87866c61d973876f84fdeea66;hp=78365e298ec7a46fcaff0603039f6410aa768c96;hpb=2d94a6ffbaacfc4d63ec47580d0caa1ae6c02e07;p=sage.d.git diff --git a/mjo/octonions.py b/mjo/octonions.py deleted file mode 100644 index 78365e2..0000000 --- a/mjo/octonions.py +++ /dev/null @@ -1,469 +0,0 @@ -from sage.misc.cachefunc import cached_method -from sage.algebras.quatalg.quaternion_algebra import QuaternionAlgebra -from sage.combinat.free_module import CombinatorialFreeModule -from sage.modules.with_basis.indexed_element import IndexedFreeModuleElement -from sage.categories.magmatic_algebras import MagmaticAlgebras -from sage.rings.all import AA, ZZ -from sage.matrix.matrix_space import MatrixSpace -from sage.misc.table import table - -from mjo.matrix_algebra import HurwitzMatrixAlgebra - -class Octonion(IndexedFreeModuleElement): - def conjugate(self): - r""" - SETUP:: - - sage: from mjo.octonions import Octonions - - EXAMPLES:: - - sage: O = Octonions() - sage: x = sum(O.gens()) - sage: x.conjugate() - e0 - e1 - e2 - e3 - e4 - e5 - e6 - e7 - - TESTS:: - - Conjugating twice gets you the original element:: - - sage: set_random_seed() - sage: O = Octonions() - sage: x = O.random_element() - sage: x.conjugate().conjugate() == x - True - - """ - C = MatrixSpace(ZZ,8).diagonal_matrix((1,-1,-1,-1,-1,-1,-1,-1)) - return self.parent().from_vector(C*self.to_vector()) - - def real(self): - r""" - Return the real part of this octonion. - - The real part of an octonion is its projection onto the span - of the first generator. In other words, the "first dimension" - is real and the others are imaginary. - - SETUP:: - - sage: from mjo.octonions import Octonions - - EXAMPLES:: - - sage: O = Octonions() - sage: x = sum(O.gens()) - sage: x.real() - e0 - - TESTS: - - This method is idempotent:: - - sage: set_random_seed() - sage: O = Octonions() - sage: x = O.random_element() - sage: x.real().real() == x.real() - True - - """ - return (self + self.conjugate())/2 - - def imag(self): - r""" - Return the imaginary part of this octonion. - - The imaginary part of an octonion is its projection onto the - orthogonal complement of the span of the first generator. In - other words, the "first dimension" is real and the others are - imaginary. - - SETUP:: - - sage: from mjo.octonions import Octonions - - EXAMPLES:: - - sage: O = Octonions() - sage: x = sum(O.gens()) - sage: x.imag() - e1 + e2 + e3 + e4 + e5 + e6 + e7 - - TESTS: - - This method is idempotent:: - - sage: set_random_seed() - sage: O = Octonions() - sage: x = O.random_element() - sage: x.imag().imag() == x.imag() - True - - """ - return (self - self.conjugate())/2 - - def _norm_squared(self): - return (self*self.conjugate()).coefficient(0) - - def norm(self): - r""" - Return the norm of this octonion. - - SETUP:: - - sage: from mjo.octonions import Octonions - - EXAMPLES:: - - sage: O = Octonions() - sage: O.one().norm() - 1 - - TESTS: - - The norm is nonnegative and belongs to the base field:: - - sage: set_random_seed() - sage: O = Octonions() - sage: n = O.random_element().norm() - sage: n >= 0 and n in O.base_ring() - True - - The norm is homogeneous:: - - sage: set_random_seed() - sage: O = Octonions() - sage: x = O.random_element() - sage: alpha = O.base_ring().random_element() - sage: (alpha*x).norm() == alpha.abs()*x.norm() - True - - """ - return self._norm_squared().sqrt() - - # The absolute value notation is typically used for complex numbers... - # and norm() isn't supported in AA, so this lets us use abs() in all - # of the division algebras we need. - abs = norm - - def inverse(self): - r""" - Return the inverse of this element if it exists. - - SETUP:: - - sage: from mjo.octonions import Octonions - - EXAMPLES:: - - sage: O = Octonions() - sage: x = sum(O.gens()) - sage: x*x.inverse() == O.one() - True - - :: - - sage: O = Octonions() - sage: O.one().inverse() == O.one() - True - - TESTS:: - - sage: set_random_seed() - sage: O = Octonions() - sage: x = O.random_element() - sage: x.is_zero() or ( x*x.inverse() == O.one() ) - True - - """ - if self.is_zero(): - raise ValueError("zero is not invertible") - return self.conjugate()/self._norm_squared() - - - def cayley_dickson(self, Q=None): - r""" - Return the Cayley-Dickson representation of this element in terms - of the quaternion algebra ``Q``. - - The Cayley-Dickson representation is an identification of - octionions `x` and `y` with pairs of quaternions `(a,b)` and - `(c,d)` respectively such that: - - * `x + y = (a+b, c+d)` - * `xy` = (ac - \bar{d}*b, da + b\bar{c})` - * `\bar{x} = (a,-b)` - - where `\bar{x}` denotes the conjugate of `x`. - - SETUP:: - - sage: from mjo.octonions import Octonions - - EXAMPLES:: - - sage: O = Octonions() - sage: x = sum(O.gens()) - sage: x.cayley_dickson() - (1 + i + j + k, 1 + i + j + k) - - """ - if Q is None: - Q = QuaternionAlgebra(self.base_ring(), -1, -1) - - i,j,k = Q.gens() - a = (self.coefficient(0)*Q.one() + - self.coefficient(1)*i + - self.coefficient(2)*j + - self.coefficient(3)*k ) - b = (self.coefficient(4)*Q.one() + - self.coefficient(5)*i + - self.coefficient(6)*j + - self.coefficient(7)*k ) - - from sage.categories.sets_cat import cartesian_product - P = cartesian_product([Q,Q]) - return P((a,b)) - - -class Octonions(CombinatorialFreeModule): - r""" - SETUP:: - - sage: from mjo.octonions import Octonions - - EXAMPLES:: - - sage: Octonions() - Octonion algebra with base ring Algebraic Real Field - sage: Octonions(field=QQ) - Octonion algebra with base ring Rational Field - - """ - def __init__(self, - field=AA, - prefix="e"): - - # Not associative, not commutative - category = MagmaticAlgebras(field).FiniteDimensional() - category = category.WithBasis().Unital() - - super().__init__(field, - range(8), - element_class=Octonion, - category=category, - prefix=prefix, - bracket=False) - - # The product of each basis element is plus/minus another - # basis element that can simply be looked up on - # https://en.wikipedia.org/wiki/Octonion - e0, e1, e2, e3, e4, e5, e6, e7 = self.gens() - self._multiplication_table = ( - (e0, e1, e2, e3, e4, e5, e6, e7), - (e1,-e0, e3,-e2, e5,-e4,-e7, e6), - (e2,-e3,-e0, e1, e6, e7,-e4,-e5), - (e3, e2,-e1,-e0, e7,-e6, e5,-e4), - (e4,-e5,-e6,-e7,-e0, e1, e2, e3), - (e5, e4,-e7, e6,-e1,-e0,-e3, e2), - (e6, e7, e4,-e5,-e2, e3,-e0,-e1), - (e7,-e6, e5, e4,-e3,-e2, e1,-e0), - ) - - def product_on_basis(self, i, j): - return self._multiplication_table[i][j] - - def one_basis(self): - r""" - Return the monomial index (basis element) corresponding to the - octonion unit element. - - SETUP:: - - sage: from mjo.octonions import Octonions - - TESTS: - - This gives the correct unit element:: - - sage: set_random_seed() - sage: O = Octonions() - sage: x = O.random_element() - sage: x*O.one() == x and O.one()*x == x - True - - """ - return 0 - - def _repr_(self): - return ("Octonion algebra with base ring %s" % self.base_ring()) - - def multiplication_table(self): - """ - Return a visual representation of this algebra's multiplication - table (on basis elements). - - SETUP:: - - sage: from mjo.octonions import Octonions - - EXAMPLES: - - The multiplication table is what Wikipedia says it is:: - - sage: Octonions().multiplication_table() - +----++----+-----+-----+-----+-----+-----+-----+-----+ - | * || e0 | e1 | e2 | e3 | e4 | e5 | e6 | e7 | - +====++====+=====+=====+=====+=====+=====+=====+=====+ - | e0 || e0 | e1 | e2 | e3 | e4 | e5 | e6 | e7 | - +----++----+-----+-----+-----+-----+-----+-----+-----+ - | e1 || e1 | -e0 | e3 | -e2 | e5 | -e4 | -e7 | e6 | - +----++----+-----+-----+-----+-----+-----+-----+-----+ - | e2 || e2 | -e3 | -e0 | e1 | e6 | e7 | -e4 | -e5 | - +----++----+-----+-----+-----+-----+-----+-----+-----+ - | e3 || e3 | e2 | -e1 | -e0 | e7 | -e6 | e5 | -e4 | - +----++----+-----+-----+-----+-----+-----+-----+-----+ - | e4 || e4 | -e5 | -e6 | -e7 | -e0 | e1 | e2 | e3 | - +----++----+-----+-----+-----+-----+-----+-----+-----+ - | e5 || e5 | e4 | -e7 | e6 | -e1 | -e0 | -e3 | e2 | - +----++----+-----+-----+-----+-----+-----+-----+-----+ - | e6 || e6 | e7 | e4 | -e5 | -e2 | e3 | -e0 | -e1 | - +----++----+-----+-----+-----+-----+-----+-----+-----+ - | e7 || e7 | -e6 | e5 | e4 | -e3 | -e2 | e1 | -e0 | - +----++----+-----+-----+-----+-----+-----+-----+-----+ - - """ - n = self.dimension() - # Prepend the header row. - M = [["*"] + list(self.gens())] - - # And to each subsequent row, prepend an entry that belongs to - # the left-side "header column." - M += [ [self.monomial(i)] + [ self.monomial(i)*self.monomial(j) - for j in range(n) ] - for i in range(n) ] - - return table(M, header_row=True, header_column=True, frame=True) - - - -class OctonionMatrixAlgebra(HurwitzMatrixAlgebra): - r""" - The algebra of ``n``-by-``n`` matrices with octonion entries over - (a subfield of) the real numbers. - - The usual matrix spaces in SageMath don't support octonion entries - because they assume that the entries of the matrix come from a - commutative and associative ring, and the octonions are neither. - - SETUP:: - - sage: from mjo.octonions import OctonionMatrixAlgebra - - EXAMPLES:: - - sage: OctonionMatrixAlgebra(3) - Module of 3 by 3 matrices with entries in Octonion algebra with base - ring Algebraic Real Field over the scalar ring Algebraic Real Field - sage: OctonionMatrixAlgebra(3,QQ) - Module of 3 by 3 matrices with entries in Octonion algebra with base - ring Rational Field over the scalar ring Rational Field - - :: - - sage: A = OctonionMatrixAlgebra(2) - sage: e0,e1,e2,e3,e4,e5,e6,e7 = A.entry_algebra().gens() - sage: A([ [e0+e4, e1+e5], - ....: [e2-e6, e3-e7] ]) - +---------+---------+ - | e0 + e4 | e1 + e5 | - +---------+---------+ - | e2 - e6 | e3 - e7 | - +---------+---------+ - - :: - - sage: A1 = OctonionMatrixAlgebra(1,QQ) - sage: A2 = OctonionMatrixAlgebra(1,QQ) - sage: cartesian_product([A1,A2]) - Module of 1 by 1 matrices with entries in Octonion algebra with - base ring Rational Field over the scalar ring Rational Field (+) - Module of 1 by 1 matrices with entries in Octonion algebra with - base ring Rational Field over the scalar ring Rational Field - - TESTS:: - - sage: set_random_seed() - sage: A = OctonionMatrixAlgebra(ZZ.random_element(10)) - sage: x = A.random_element() - sage: x*A.one() == x and A.one()*x == x - True - - """ - def __init__(self, n, scalars=AA, prefix="E", **kwargs): - super().__init__(Octonions(field=scalars), - scalars, - n, - prefix=prefix, - **kwargs) - -class QuaternionMatrixAlgebra(HurwitzMatrixAlgebra): - r""" - The algebra of ``n``-by-``n`` matrices with quaternion entries over - (a subfield of) the real numbers. - - The usual matrix spaces in SageMath don't support quaternion entries - because they assume that the entries of the matrix come from a - commutative ring, and the quaternions are not commutative. - - SETUP:: - - sage: from mjo.octonions import QuaternionMatrixAlgebra - - EXAMPLES:: - - sage: QuaternionMatrixAlgebra(3) - Module of 3 by 3 matrices with entries in Quaternion - Algebra (-1, -1) with base ring Algebraic Real Field - over the scalar ring Algebraic Real Field - sage: QuaternionMatrixAlgebra(3,QQ) - Module of 3 by 3 matrices with entries in Quaternion - Algebra (-1, -1) with base ring Rational Field over - the scalar ring Rational Field - - :: - - sage: A = QuaternionMatrixAlgebra(2) - sage: i,j,k = A.entry_algebra().gens() - sage: A([ [1+i, j-2], - ....: [k, k+j] ]) - +-------+--------+ - | 1 + i | -2 + j | - +-------+--------+ - | k | j + k | - +-------+--------+ - - :: - - sage: A1 = QuaternionMatrixAlgebra(1,QQ) - sage: A2 = QuaternionMatrixAlgebra(2,QQ) - sage: cartesian_product([A1,A2]) - Module of 1 by 1 matrices with entries in Quaternion Algebra - (-1, -1) with base ring Rational Field over the scalar ring - Rational Field (+) Module of 2 by 2 matrices with entries in - Quaternion Algebra (-1, -1) with base ring Rational Field over - the scalar ring Rational Field - - TESTS:: - - sage: set_random_seed() - sage: A = QuaternionMatrixAlgebra(ZZ.random_element(10)) - sage: x = A.random_element() - sage: x*A.one() == x and A.one()*x == x - True - - """ - def __init__(self, n, scalars=AA, **kwargs): - # The -1,-1 gives us the "usual" definition of quaternion - Q = QuaternionAlgebra(scalars,-1,-1) - super().__init__(Q, scalars, n, **kwargs)