Hot-keys on this page
r m x p toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
""" Elements of Quaternion Algebras
Sage allows for computation with elements of quaternion algebras over a nearly arbitrary base field of characteristic not 2. Sage also has very highly optimized implementation of arithmetic in rational quaternion algebras and quaternion algebras over number fields.
TESTS:
Check that :trac:`20829` is fixed::
sage: D.<i,j,k>=QuaternionAlgebra(QQ,-1,-3) sage: hash(i) 184301497 """
#***************************************************************************** # Copyright (C) 2009 William Stein <wstein@gmail.com> # Copyright (C) 2009 Jonathan Bober <jwbober@gmail.com> # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 2 of the License, or # (at your option) any later version. # http://www.gnu.org/licenses/ #*****************************************************************************
from sage.structure.element cimport AlgebraElement, RingElement, ModuleElement, Element from sage.algebras.quatalg.quaternion_algebra_element cimport QuaternionAlgebraElement_abstract from sage.rings.rational cimport Rational from sage.rings.integer cimport Integer from sage.rings.polynomial.polynomial_integer_dense_flint cimport Polynomial_integer_dense_flint from sage.rings.number_field.number_field_element cimport NumberFieldElement from sage.rings.all import PolynomialRing from sage.matrix.all import matrix
from sage.libs.gmp.mpz cimport * from sage.libs.gmp.mpq cimport * from sage.libs.ntl.convert cimport mpz_to_ZZ, ZZ_to_mpz from sage.libs.flint.fmpz cimport * from sage.libs.flint.fmpz_poly cimport * from sage.libs.flint.ntl_interface cimport *
# variables for holding temporary values computed in # QuaternionAlgebraElement_rational_field._mul_() cdef mpz_t T1, T2, t3, t4, t5, t6, t7, t8, s1, s2, U1, U2 cdef fmpz_poly_t fT1, fT2, ft3, ft4, ft5, ft6, ft7, ft8, fs1, fs2, fU1, fU2
def _init_globals(): """ Clear all global variables allocated for optimization of quaternion algebra arithmetic.
Do *not* call this unless you want to leak memory.
EXAMPLES::
sage: sage.algebras.quatalg.quaternion_algebra_element._clear_globals() sage: sage.algebras.quatalg.quaternion_algebra_element._init_globals() """ # over QQ
# Number fields
# Initialize module-scope global C variables. _init_globals()
def _clear_globals(): """ Clear all global variables allocated for optimization of quaternion algebra arithmetic.
Do *not* call this except on exit of Sage, unless you want to see segfaults.
This is called in the function quit_sage(), which is defined in all.py.
EXAMPLES::
sage: sage.algebras.quatalg.quaternion_algebra_element._clear_globals() sage: sage.algebras.quatalg.quaternion_algebra_element._init_globals() """
cdef to_quaternion(R, x): """ Internal function used implicitly by quaternion algebra creation.
INPUT:
- R -- callable - x -- element or 4-tuple
Given a callable R and an x that defines a quaternion, which can be a 4-tuple, list of length 4, or something that coerces to R, return 4-tuple of elements of R.
EXAMPLES:: sage: Q.<i,j,kkkk> = QuaternionAlgebra(QQ,-7, 13) sage: kkkk._repr_() # implicit doctest 'kkkk' """ else:
cdef inline print_coeff(y, i, bint atomic): """ Internal function used implicitly by all quaternion algebra printing.
INPUT:
- y -- coefficient - i -- string (name of a generator) - atomic -- boolean int; whether or not elements of base ring print atomically
EXAMPLES::
sage: Q.<i,j,k> = QuaternionAlgebra(QQ,-7, 13) sage: i._repr_() # implicit doctest 'i' """ else:
cdef class QuaternionAlgebraElement_abstract(AlgebraElement): def __hash__(self): r""" TESTS::
sage: from itertools import product sage: for K in [QQ, QuadraticField(2), AA, Frac(QQ['x'])]: ....: Q.<i,j,k> = QuaternionAlgebra(K,-5,-2) ....: assert hash(Q.one()) == hash(K.one()) ....: assert hash(Q(2)) == hash(K(2)) ....: elts = [] ....: for (x,y,z,w) in product([K(0), K(1), K(2), K(-1)], repeat=4): ....: elts.append(x + y*i + z*j + w*k) ....: assert len(set(map(hash, elts))) == len(elts) """ cdef long h
cpdef bint is_constant(self): """ Return True if this quaternion is constant, i.e., has no i, j, or k term.
OUTPUT: bool
EXAMPLES::
sage: A.<i,j,k>=QuaternionAlgebra(-1,-2) sage: A(1).is_constant() True sage: A(1+i).is_constant() False sage: A(i).is_constant() False """
def __int__(self): """ Try to coerce this quaternion to a Python int.
EXAMPLES:: sage: A.<i,j,k>=QuaternionAlgebra(-1,-2) sage: int(A(-3)) -3 sage: int(A(-3/2)) -1 sage: int(-3 + i) Traceback (most recent call last): ... TypeError """
def __long__(self): """ Try to coerce this quaternion to a Python long.
EXAMPLES::
sage: A.<i,j,k>=QuaternionAlgebra(-1,-2) sage: long(A(-3)) -3L sage: long(A(-3/2)) -1L sage: long(-3 + i) Traceback (most recent call last): ... TypeError """
def __float__(self): """ Try to coerce this quaternion to a Python float.
EXAMPLES::
sage: A.<i,j,k>=QuaternionAlgebra(-1,-2) sage: float(A(-3/2)) -1.5 sage: float(A(-3)) -3.0 sage: float(-3 + i) Traceback (most recent call last): ... TypeError """
def _integer_(self, ZZ=None): """ Try to coerce this quaternion to an Integer.
EXAMPLES:: sage: A.<i,j,k>=QuaternionAlgebra(-1,-2) sage: Integer(A(-3)) # indirect doctest -3 sage: type(Integer(A(-3))) <... 'sage.rings.integer.Integer'> sage: Integer(A(-3/2)) Traceback (most recent call last): ... TypeError: no conversion of this rational to integer sage: Integer(-3 + i) Traceback (most recent call last): ... TypeError """
def _rational_(self): """ Try to coerce this quaternion to a Rational.
EXAMPLES::
sage: Q.<i,j,k> = QuaternionAlgebra(Frac(QQ['x']),-5,-2) sage: Rational(Q(2/3)) # indirect doctest 2/3 sage: Rational(2/3 + i) Traceback (most recent call last): ... TypeError """
def __nonzero__(self): """ Return True if this quaternion is nonzero.
EXAMPLES::
sage: Q.<i,j,k> = QuaternionAlgebra(Frac(QQ['x']),-5,-2) sage: bool(i+j) True sage: (i+j).__nonzero__() True sage: Q(0).__nonzero__() False """
cdef _do_print(self, x,y,z,w): """ Used internally by the print function.
EXAMPLES::
sage: Q.<i,j,k> = QuaternionAlgebra(-17,-19) sage: str(i+j+k-3/4) # indirect doctest '-3/4 + i + j + k' """
def _repr_(self): """ Return string representation of this quaternion:
EXAMPLES::
sage: R.<x> = Frac(QQ['x']); Q.<i,j,k> = QuaternionAlgebra(R,-5*x,-2) sage: a = x + i*x^3 + j*x^2 + k*x sage: a._repr_() 'x + x^3*i + x^2*j + x*k' sage: a = x+2/3 + i*x^3 + j*(x^2-5/2) + k*x sage: a._repr_() 'x + 2/3 + x^3*i + (x^2 - 5/2)*j + x*k' sage: type(a) <... 'sage.algebras.quatalg.quaternion_algebra_element.QuaternionAlgebraElement_generic'> sage: Q(0)._repr_() '0' """
cpdef int _cmp_(self, right) except -2: """ Comparing elements.
TESTS::
sage: Q.<i,j,k> = QuaternionAlgebra(Frac(QQ['x']),-5,-2) sage: a = 1/2 + 2/3*i - 3/4*j + 5/7*k sage: a == a True sage: a == a + 1 False sage: a < a + 1 True
sage: K.<x> = QQ[]; Q.<i,j,k> = QuaternionAlgebra(x, 2*x); a = x + 2*x*i + 3*j + (x-2)*k sage: (1/a)*a == 1 True sage: (1/a)*a 1 """ cdef int i return 1
cpdef conjugate(self): """ Return the conjugate of the quaternion: if `\\theta = x + yi + zj + wk`, return `x - yi - zj - wk`; that is, return theta.reduced_trace() - theta.
EXAMPLES::
sage: A.<i,j,k> = QuaternionAlgebra(QQ,-5,-2) sage: a = 3*i - j + 2 sage: type(a) <... 'sage.algebras.quatalg.quaternion_algebra_element.QuaternionAlgebraElement_rational_field'> sage: a.conjugate() 2 - 3*i + j
The "universal" test::
sage: K.<x,y,z,w,a,b> = QQ[] sage: Q.<i,j,k> = QuaternionAlgebra(a,b) sage: theta = x+y*i+z*j+w*k sage: theta.conjugate() x + (-y)*i + (-z)*j + (-w)*k """
cpdef reduced_trace(self): """ Return the reduced trace of self: if `\\theta = x + yi + zj + wk`, then `\\theta` has reduced trace `2x`.
EXAMPLES::
sage: K.<x,y,z,w,a,b> = QQ[] sage: Q.<i,j,k> = QuaternionAlgebra(a,b) sage: theta = x+y*i+z*j+w*k sage: theta.reduced_trace() 2*x """
cpdef reduced_norm(self): """ Return the reduced norm of self: if `\\theta = x + yi + zj + wk`, then `\\theta` has reduced norm `x^2 - ay^2 - bz^2 + abw^2`.
EXAMPLES::
sage: K.<x,y,z,w,a,b> = QQ[] sage: Q.<i,j,k> = QuaternionAlgebra(a,b) sage: theta = x+y*i+z*j+w*k sage: theta.reduced_norm() w^2*a*b - y^2*a - z^2*b + x^2 """
def __invert__(self): """ Return inverse of self.
EXAMPLES::
sage: Q.<i,j,k> = QuaternionAlgebra(QQ,-7,-13) sage: theta = 1/3 - 2/3*i + 4/19*j - 17/3*k sage: (1/theta) * theta 1 sage: type(theta) <... 'sage.algebras.quatalg.quaternion_algebra_element.QuaternionAlgebraElement_rational_field'> sage: 1/Q(0) Traceback (most recent call last): ... ZeroDivisionError: rational division by zero
Note that the quaternion algebra need not be a division algebra, in which case we can get a ZeroDivisionException::
sage: Q.<i,j,k> = QuaternionAlgebra(QQ,4,9) sage: theta = 2-i sage: theta.reduced_norm() 0 sage: 1/theta Traceback (most recent call last): ... ZeroDivisionError: rational division by zero
The ``universal`` test:
sage: K.<x,y,z,w,a,b> = QQ[] sage: Q.<i,j,k> = QuaternionAlgebra(a,b) sage: theta = x+y*i+z*j+w*k sage: 1/theta == theta.conjugate()/theta.reduced_norm() True """
cpdef _rmul_(self, Element left): """ Return left*self, where left is in the base ring.
EXAMPLES::
sage: K.<a> = NumberField(x^2-x-1); Q.<i,j,k> = QuaternionAlgebra(K,-1,-1); z=2*i+3*j+4/3*k+5/8 sage: a*z 5/8*a + 2*a*i + 3*a*j + 4/3*a*k sage: type(z) <... 'sage.algebras.quatalg.quaternion_algebra_element.QuaternionAlgebraElement_generic'> """
cpdef _lmul_(self, Element right): """ Return self*right, where right is in the base ring.
EXAMPLES::
sage: K.<a> = NumberField(x^2-x-1); Q.<i,j,k> = QuaternionAlgebra(K,-1,-1); z=2*i+3*j+4/3*k+5/8 sage: z*a 5/8*a + 2*a*i + 3*a*j + 4/3*a*k sage: type(z) <... 'sage.algebras.quatalg.quaternion_algebra_element.QuaternionAlgebraElement_generic'> """
cpdef _div_(self, right): """ Return quotient of self by right.
EXAMPLES::
sage: K.<x> = QQ[]; Q.<i,j,k> = QuaternionAlgebra(x, 2*x); sage: theta = x + 2*x*i + 3*j + (x-2)*k sage: type(theta) <... 'sage.algebras.quatalg.quaternion_algebra_element.QuaternionAlgebraElement_generic'> sage: theta._div_(theta) 1 sage: theta._div_(theta) == 1 True """
def reduced_characteristic_polynomial(self, var='x'): """ Return the reduced characteristic polynomial of this quaternion algebra element, which is `X^2 - tX + n`, where `t` is the reduced trace and `n` is the reduced norm.
INPUT:
- var -- string (default: 'x'); indeterminate of characteristic polynomial
EXAMPLES::
sage: A.<i,j,k>=QuaternionAlgebra(-1,-2) sage: i.reduced_characteristic_polynomial() x^2 + 1 sage: j.reduced_characteristic_polynomial() x^2 + 2 sage: (i+j).reduced_characteristic_polynomial() x^2 + 3 sage: (2+j+k).reduced_trace() 4 sage: (2+j+k).reduced_characteristic_polynomial('T') T^2 - 4*T + 8 """
def matrix(self, action='right'): """ Return the matrix of right or left multiplication of self on the basis for the ambient quaternion algebra. In particular, if action is 'right' (the default), returns the matrix of the mapping sending x to x*self.
INPUT:
- ``action`` -- (default: 'right') 'right' or 'left'.
OUTPUT:
- a matrix
EXAMPLES::
sage: Q.<i,j,k> = QuaternionAlgebra(-3,-19) sage: a = 2/3 -1/2*i + 3/5*j - 4/3*k sage: a.matrix() [ 2/3 -1/2 3/5 -4/3] [ 3/2 2/3 4 3/5] [-57/5 -76/3 2/3 1/2] [ 76 -57/5 -3/2 2/3] sage: a.matrix() == a.matrix(action='right') True sage: a.matrix(action='left') [ 2/3 -1/2 3/5 -4/3] [ 3/2 2/3 -4 -3/5] [-57/5 76/3 2/3 -1/2] [ 76 57/5 3/2 2/3] sage: (i*a,j*a,k*a) (3/2 + 2/3*i + 4*j + 3/5*k, -57/5 - 76/3*i + 2/3*j + 1/2*k, 76 - 57/5*i - 3/2*j + 2/3*k) sage: a.matrix(action='foo') Traceback (most recent call last): ... ValueError: action must be either 'left' or 'right'
We test over a more generic base field::
sage: K.<x> = QQ['x'] sage: Q.<i,j,k> = QuaternionAlgebra(Frac(K),-5,-2) sage: a = 1/2*x^2 + 2/3*x*i - 3/4*j + 5/7*k sage: type(a) <... 'sage.algebras.quatalg.quaternion_algebra_element.QuaternionAlgebraElement_generic'> sage: a.matrix() [1/2*x^2 2/3*x -3/4 5/7] [-10/3*x 1/2*x^2 -25/7 -3/4] [ 3/2 10/7 1/2*x^2 -2/3*x] [ -50/7 3/2 10/3*x 1/2*x^2] """ else:
def coefficient_tuple(self): """ Return 4-tuple of coefficients of this quaternion.
EXAMPLES::
sage: K.<x> = QQ['x'] sage: Q.<i,j,k> = QuaternionAlgebra(Frac(K),-5,-2) sage: a = 1/2*x^2 + 2/3*x*i - 3/4*j + 5/7*k sage: type(a) <... 'sage.algebras.quatalg.quaternion_algebra_element.QuaternionAlgebraElement_generic'> sage: a.coefficient_tuple() (1/2*x^2, 2/3*x, -3/4, 5/7) """
def pair(self, right): """ Return the result of pairing self and right, which should both be elements of a quaternion algebra. The pairing is (x,y) = (x.conjugate()*y).reduced_trace().
INPUT:
- ``right`` -- quaternion
EXAMPLES::
sage: A.<i,j,k>=QuaternionAlgebra(-1,-2) sage: (1+i+j-2*k).pair(2/3+5*i-3*j+k) -26/3 sage: x = 1+i+j-2*k; y = 2/3+5*i-3*j+k sage: x.pair(y) -26/3 sage: y.pair(x) -26/3 sage: (x.conjugate()*y).reduced_trace() -26/3 """
cdef class QuaternionAlgebraElement_generic(QuaternionAlgebraElement_abstract): """ TESTS:
We test pickling::
sage: R.<x> = Frac(QQ['x']); Q.<i,j,k> = QuaternionAlgebra(R,-5*x,-2) sage: theta = x + i*x^3 + j*x^2 + k*x sage: theta == loads(dumps(theta)) True """ def __init__(self, parent, v, bint check=True): """ Create a quaternion over some general base field.
EXAMPLES::
sage: K.<x> = Frac(QQ['x']); Q.<i,j,k> = QuaternionAlgebra(K,-5,-2) sage: sage.algebras.quatalg.quaternion_algebra_element.QuaternionAlgebraElement_generic(Q, (x,1,-7,2/3*x^3)) x + i + (-7)*j + 2/3*x^3*k """ else:
def __getitem__(self, int i): """ EXAMPLES::
sage: Q.<i,j,k> = QuaternionAlgebra(Frac(QQ['x']),-5,-2) sage: theta = 1/2 + 2/3*i - 3/4*j + 5/7*k sage: type(theta) <... 'sage.algebras.quatalg.quaternion_algebra_element.QuaternionAlgebraElement_generic'> sage: list(theta) [1/2, 2/3, -3/4, 5/7] """ elif i == 1: elif i == 2: elif i == 3: else:
def __reduce__(self): """ Used for pickling.
TESTS:: sage: K.<x> = Frac(QQ['x']); Q.<i,j,k> = QuaternionAlgebra(K,-5,-2) sage: theta = 1/x + x*i - (x+1)*j + 2/(3*x^3+5)*k sage: loads(dumps(theta)) == theta True sage: type(theta) <... 'sage.algebras.quatalg.quaternion_algebra_element.QuaternionAlgebraElement_generic'> """
cpdef _add_(self, _right): """ Return the sum of self and _right.
EXAMPLES::
sage: K.<x> = Frac(QQ['x']); Q.<i,j,k> = QuaternionAlgebra(K,-5,-2) sage: (x+i+j+x^3*k) + (x-i-j+ (2/3*x^3+x)*k) # indirect doctest 2*x + (5/3*x^3 + x)*k sage: type(i) <... 'sage.algebras.quatalg.quaternion_algebra_element.QuaternionAlgebraElement_generic'> """ # TODO -- make this, etc. use __new__
cpdef _sub_(self, _right): """ Return the difference of self and _right.
EXAMPLES::
sage: K.<x> = Frac(QQ['x']); Q.<i,j,k> = QuaternionAlgebra(K,-5,-2) sage: type(i) <... 'sage.algebras.quatalg.quaternion_algebra_element.QuaternionAlgebraElement_generic'> sage: (x+i+j+x^3*k)._sub_(x-i-j+ (2/3*x^3+x)*k) 2*i + 2*j + (1/3*x^3 - x)*k """
cpdef _mul_(self, _right): """ Return the product of self and _right.
EXAMPLES:: sage: K.<x> = Frac(QQ['x']); Q.<i,j,k> = QuaternionAlgebra(K,-5,-2) sage: type(i) <... 'sage.algebras.quatalg.quaternion_algebra_element.QuaternionAlgebraElement_generic'> sage: (x+i+j+x^3*k)._mul_(x-i-j+ (2/3*x^3+x)*k) -20/3*x^6 - 10*x^4 + x^2 + 7 + (10/3*x^3 + 2*x)*i + (-25/3*x^3 - 5*x)*j + (5/3*x^4 + x^2)*k """
#x = x1*x2 + y1*y2*a + z1*z2*b - w1*w2*a*b #y = x1*y2 + y1*x2 - z1*w2*b + w1*z2*b #z = x1*z2 + y1*w2 + z1*x2 - w1*y2*a #w = x1*w2 + y1*z2 - z1*y2 + w1*x2
def _repr_(self): """ Print representation of self.
EXAMPLES::
sage: K.<x> = Frac(QQ['x']); Q.<i,j,k> = QuaternionAlgebra(K,-5,-2) sage: theta = 1/x + x*i - (x+1)*j + 2/(3*x^3+5)*k sage: type(theta) <... 'sage.algebras.quatalg.quaternion_algebra_element.QuaternionAlgebraElement_generic'> sage: theta._repr_() '1/x + x*i + (-x - 1)*j + (2/(3*x^3 + 5))*k' """
cdef class QuaternionAlgebraElement_rational_field(QuaternionAlgebraElement_abstract): """ TESTS:
We test pickling::
sage: Q.<i,j,k> = QuaternionAlgebra(QQ,-5,-2) sage: i + j + k == loads(dumps(i+j+k)) True """
# Implementation Notes: # # A Quaternion algebra element (call it a) over Q are implemented as a 4-tuple of # integers x, y, z, w and a denominator d, all of type mpz_t, such that # # theta = (1/d) * (x + y * i + z * j + w * k) # # (although different letters may be specified instead of i, j, and k, if desired). # # Inside the element we also store mpz_t integers a and b, where # # i^2 = a and j^2 = b
def __cinit__(self): """ Initialize C variables.
EXAMPLES::
sage: QuaternionAlgebra(QQ,-5,-2)([1/2,-1/3,2/3,4/5]) # implicit doctest 1/2 - 1/3*i + 2/3*j + 4/5*k """
def __dealloc__(self):
def _rational_(self): """ Try to coerce this quaternion to a Rational.
EXAMPLES::
sage: A.<i,j,k> = QuaternionAlgebra(-1,-2) sage: Rational(A(-2/3)) # indirect doctest -2/3 sage: Rational(i) Traceback (most recent call last): ... TypeError """
cpdef bint is_constant(self): """ Return True if this quaternion is constant, i.e., has no i, j, or k term.
OUTPUT: bool
EXAMPLES::
sage: A.<i,j,k>=QuaternionAlgebra(-1,-2) sage: A(1/3).is_constant() True sage: A(-1).is_constant() True sage: (1+i).is_constant() False sage: j.is_constant() False """
def __nonzero__(self): """ Return True if this quaternion is nonzero.
EXAMPLES::
sage: A.<i,j,k>=QuaternionAlgebra(-1,-2) sage: bool(1+j+k) True sage: A(0).__nonzero__() False """
cpdef int _cmp_(self, _right) except -2: """ Compare two quaternions. The comparison is fairly arbitrary -- first the denominators are compared and if equal then each of the other coefficients are compared.
TESTS::
sage: Q.<i,j,k> = QuaternionAlgebra(QQ,-5,-2) sage: i < j False sage: -i < j True sage: i == i True """ cdef int i
def __init__(self, parent, v, bint check=True): """ Setup element data from parent and coordinates.
EXAMPLES::
sage: A.<i,j,k>=QuaternionAlgebra(-4,-5) sage: A(2/3) 2/3 sage: type(A(2/3)) <... 'sage.algebras.quatalg.quaternion_algebra_element.QuaternionAlgebraElement_rational_field'>
sage: A([-1/2,-10/3,-2/3,-4/5]) # implicit doctest -1/2 - 10/3*i - 2/3*j - 4/5*k sage: A(vector([1,2/3,3/4,4/5])) 1 + 2/3*i + 3/4*j + 4/5*k
::
sage: QA = QuaternionAlgebra(QQ, -1, -1) sage: foo = QA(3.0); foo 3 sage: parent(foo) Quaternion Algebra (-1, -1) with base ring Rational Field sage: foo[0] 3 sage: parent(foo[0]) Rational Field """
# cache a and b
cdef Rational x, y, z, w cdef mpz_t lcm cdef mpq_t lcm_rat pass # Now v is definitely a list or tuple, and we convert each # entry to a rational, then clear denominators, etc. else:
def __getitem__(self, int i): """ TESTS::
sage: Q.<i,j,k> = QuaternionAlgebra(QQ,-5,-2) sage: theta = 1/2 + 2/3*i - 3/4*j + 5/7*k sage: type(theta) <... 'sage.algebras.quatalg.quaternion_algebra_element.QuaternionAlgebraElement_rational_field'> sage: list(theta) [1/2, 2/3, -3/4, 5/7] """ elif i == 1: elif i == 2: elif i == 3: else:
def __reduce__(self): """ Used for pickling.
TESTS::
sage: K.<x> = QQ[] sage: Q.<i,j,k> = QuaternionAlgebra(Frac(K),-5,-19) sage: theta = 1/2 + 2/3*i - 3/4*j + 5/7*k sage: type(theta) <... 'sage.algebras.quatalg.quaternion_algebra_element.QuaternionAlgebraElement_generic'> sage: loads(dumps(theta)) == theta True
"""
cpdef _add_(self, _right): """ EXAMPLES::
sage: Q.<i,j,k> = QuaternionAlgebra(15) sage: type(i) <... 'sage.algebras.quatalg.quaternion_algebra_element.QuaternionAlgebraElement_rational_field'> sage: (2/3 + 3/4*i + 5/6*j + 7/8*k)._add_(-2/3 - 3/4*i + 5/6*j + 7/8*k) 5/3*j + 7/4*k """
# Given two quaternion algebra elements # theta = (1/d1)*(x1 + y1 * i + z1 * j + w1 * k) # nu = (1/d2)*(x2 + y2 * i + z2 * j + w2 * k) # # we compute their sum as # # (theta + nu) = (1/d3)*(x3 + y3 * i + z3 * j + w3 * k) # # with d3 = d1 * d2 # x3 = d1 * x2 + d2 * x1 # y3 = d1 * y2 + d2 * y1 # z3 = d1 * z2 + d2 * z1 # w3 = d1 * w2 + d2 * w1 # # and then we reduce the sum by dividing everything # by the gcd of d3, x3, y3, z3, and w3
cpdef _sub_(self, _right): """ EXAMPLES::
sage: Q.<i,j,k> = QuaternionAlgebra(15) sage: type(i) <... 'sage.algebras.quatalg.quaternion_algebra_element.QuaternionAlgebraElement_rational_field'> sage: (2/3 + 3/4*i + 5/6*j + 7/8*k)._sub_(-2/3 - 3/4*i + 5/6*j + 7/8*k) 4/3 + 3/2*i """
# Implementation Note: To obtain _sub_, we simply replace every occurrence of # "add" in _add_ with "sub"; that is, we s/add/sub to get _sub_
cpdef _mul_(self, _right): """ EXAMPLES::
sage: Q.<i,j,k> = QuaternionAlgebra(15) sage: type(i) <... 'sage.algebras.quatalg.quaternion_algebra_element.QuaternionAlgebraElement_rational_field'> sage: (2/3 + 3/4*i + 5/6*j + 7/8*k)._mul_(-2/3 - 3/4*i + 5/6*j + 7/8*k) 9331/576 - i - 63/16*j + 5/4*k """
# We use the following formula for multiplication: # # Given two quaternion algebra elements # # theta = (1/d1)*(x1 + y1 * i + z1 * j + w1 * k) # nu = (1/d2)*(x2 + y2 * i + z2 * j + w2 * k) # # we compute their product as # # theta*nu = (1/d3)*(x3 + y3 * i + z3 * j + w3 * k) # # where # x3 = t1 + a * t2 + b * (t3 - a*t4) # y3 = s1*(x2 + y2) - t1 - t2 + b*( s2*(z2 - w2) - t3 + t4) # z3 = t5 - a*t6 + t7 + a*t8 # w3 = (x2 - y2)*s2 - t5 + t6 + s1*(z2 + w2) - t7 - t8 # # and where # t1 = x1 * x2 # t2 = y1 * y2 # t3 = z1 * z2 # t4 = w1 * w2 # t5 = x2 * z1 # t6 = y2 * w1 # t7 = x1 * z2 # t8 = y1 * w2 # # s1 = x1 + y1 # s2 = z1 + w1 # # This takes more integer addition operations but fewer integer multiplication # operations than the "straightforward" multiplication method. # # There might be a way to optimize this formula further.
#------------------
#------------------
#------------------
#------------------
cpdef reduced_norm(self): """ Return the reduced norm of self. Given a quaternion `x+yi+zj+wk`, this is `x^2 - ay^2 - bz^2 + abw^2`.
EXAMPLES::
sage: K.<i,j,k> = QuaternionAlgebra(QQ, -5, -2) sage: i.reduced_norm() 5 sage: j.reduced_norm() 2 sage: a = 1/3 + 1/5*i + 1/7*j + k sage: a.reduced_norm() 22826/2205 """
cpdef conjugate(self): """ Return the conjugate of this quaternion.
EXAMPLES::
sage: A.<i,j,k> = QuaternionAlgebra(QQ,-5,-2) sage: a = 3*i - j + 2 sage: type(a) <... 'sage.algebras.quatalg.quaternion_algebra_element.QuaternionAlgebraElement_rational_field'> sage: a.conjugate() 2 - 3*i + j sage: b = 1 + 1/3*i + 1/5*j - 1/7*k sage: b.conjugate() 1 - 1/3*i - 1/5*j + 1/7*k """
cpdef reduced_trace(self): """ Return the reduced trace of self, which is `2x` if self is `x+iy+zj+wk`.
EXAMPLES::
sage: K.<i,j,k> = QuaternionAlgebra(QQ, -5, -2) sage: i.reduced_trace() 0 sage: j.reduced_trace() 0 sage: a = 1/3 + 1/5*i + 1/7*j + k sage: a.reduced_trace() 2/3 """ #return 2*self[0]
cdef inline canonicalize(self): """ Put the representation of this quaternion element into smallest form. For `a = (1/d)(x + yi + zj + wk)` we divide `a`, `x`, `y`, `z`, and `w` by the gcd of all of them.
TESTS:: sage: K.<i,j,k> = QuaternionAlgebra(QQ, -10, -7) sage: (1/4 + 1/2 * i + 1/7 * j + 1/28 * k)*14*i # implicit doctest -70 + 7/2*i + 5*j - 2*k """
# Note: this function changes the module-level global variable # U1, so it isn't always safe to use this in the middle of # another function. Normally this function is called # at the end of an arithmetic routine, so this is fine.
# Implementation-wise, we compute the GCD's one at a time, # and quit if it ever becomes one
# at this point U1 actually contains the gcd of all the terms, and we divide
def denominator(self): """ Return the lowest common multiple of the denominators of the coefficients of i, j and k for this quaternion.
EXAMPLES::
sage: A = QuaternionAlgebra(QQ, -1, -1) sage: A.<i,j,k> = QuaternionAlgebra(QQ, -1, -1) sage: a = (1/2) + (1/5)*i + (5/12)*j + (1/13)*k sage: a 1/2 + 1/5*i + 5/12*j + 1/13*k sage: a.denominator() 780 sage: lcm([2, 5, 12, 13]) 780 sage: (a * a).denominator() 608400 sage: (a + a).denominator() 390 """
def denominator_and_integer_coefficient_tuple(self): """ Return 5-tuple d, x, y, z, w, where this rational quaternion is equal to `(x + yi + zj + wk)/d` and x, y, z, w do not share a common factor with d.
OUTPUT: 5-tuple of Integers
EXAMPLES::
sage: A.<i,j,k>=QuaternionAlgebra(-1,-2) sage: (2 + 3*i + 4/3*j - 5*k).denominator_and_integer_coefficient_tuple() (3, 6, 9, 4, -15) """
def integer_coefficient_tuple(self): """ Returns integer part of this quaternion, ignoring the common denominator.
OUTPUT: 4-tuple of Integers
EXAMPLES::
sage: A.<i,j,k>=QuaternionAlgebra(-1,-2) sage: (2 + 3*i + 4/3*j - 5*k).integer_coefficient_tuple() (6, 9, 4, -15) """
def coefficient_tuple(self): """ Return 4-tuple of rational numbers which are the coefficients of this quaternion.
EXAMPLES::
sage: A.<i,j,k>=QuaternionAlgebra(-1,-2) sage: (2/3 + 3/5*i + 4/3*j - 5/7*k).coefficient_tuple() (2/3, 3/5, 4/3, -5/7) """
def _multiply_by_integer(self, Integer n): """ Return the product of self times the integer n.
EXAMPLES:: sage: A = QuaternionAlgebra(7) sage: a = A.random_element() sage: 5*a == a._multiply_by_integer(5) True """
if mpz_divisible_p(n.value, self.d): mpz_divexact(T1, n.value, self.d) else: mpz_set(T1, n.value)
mpz_set(result.d, self.d) mpz_mul(result.x, self.x, T1) mpz_mul(result.y, self.y, T1) mpz_mul(result.z, self.z, T1) mpz_mul(result.w, self.w, T1)
return result
def _divide_by_integer(self, Integer n): """ Return the quotient of self by the integer n.
EXAMPLES:: sage: A = QuaternionAlgebra(7) sage: a = A.random_element() sage: a/5 == a._divide_by_integer(5) True sage: a._divide_by_integer(0) Traceback (most recent call last): ... ZeroDivisionError """
cdef class QuaternionAlgebraElement_number_field(QuaternionAlgebraElement_abstract): def __cinit__(self): """ Allocate memory for this quaternion over a number field.
EXAMPLES::
sage: K.<a> = QQ[2^(1/5)]; Q.<i,j,k> = QuaternionAlgebra(K,-a,a*17/3) sage: Q([a,-2/3,a^2-1/2,a*2]) # implicit doctest a + (-2/3)*i + (a^2 - 1/2)*j + 2*a*k """
def __dealloc__(self): """ Free memory used by this quaternion over a number field. """
def __init__(self, parent, v, bint check=True): """ EXAMPLES::
sage: K.<a> = QQ[2^(1/3)]; Q.<i,j,k> = QuaternionAlgebra(K,-a,a+1) sage: Q([a,-2/3,a^2-1/2,a*2]) # implicit doctest a + (-2/3)*i + (a^2 - 1/2)*j + 2*a*k """ else:
def __getitem__(self, int i): """ EXAMPLES::
sage: K.<a> = QQ[2^(1/3)]; Q.<i,j,k> = QuaternionAlgebra(K,-a,a+1) sage: Q([a,-2/3,a^2-1/2,a*2]) a + (-2/3)*i + (a^2 - 1/2)*j + 2*a*k sage: x = Q([a,-2/3,a^2-1/2,a*2]) sage: type(x) <... 'sage.algebras.quatalg.quaternion_algebra_element.QuaternionAlgebraElement_number_field'> sage: x[0] a sage: x[1] -2/3 sage: x[2] a^2 - 1/2 sage: x[3] 2*a sage: list(x) [a, -2/3, a^2 - 1/2, 2*a] """ # general number -- this code assumes that the number field is not quadratic!!
elif i == 1: elif i == 2: elif i == 3: else:
def __reduce__(self): """ EXAMPLES::
sage: K.<a> = QQ[2^(1/3)]; Q.<i,j,k> = QuaternionAlgebra(K, -3, a) sage: z = (i+j+k+a)^2; z a^2 + 4*a - 3 + 2*a*i + 2*a*j + 2*a*k sage: f, t = z.__reduce__() sage: f(*t) a^2 + 4*a - 3 + 2*a*i + 2*a*j + 2*a*k sage: loads(dumps(z)) == z True """
cpdef _add_(self, _right): """ Add self and _right:
EXAMPLES::
sage: K.<a> = QQ[2^(1/3)]; Q.<i,j,k> = QuaternionAlgebra(K, -3, a) sage: z = a + i + (2/3)*a^3*j + (1+a)*k; w = a - i - (2/3)*a^3*j + (1/3+a)*k sage: type(z) <... 'sage.algebras.quatalg.quaternion_algebra_element.QuaternionAlgebraElement_number_field'> sage: z._add_(w) 2*a + (2*a + 4/3)*k
Check that the fix in :trac:`17099` is correct::
sage: K = NumberField(x**3 + x - 1, 'a') sage: D.<i,j,k> = QuaternionAlgebra(K, -1, -3) sage: j/3 + (2*j)/3 == j True """
# Given two quaternion algebra elements # a = (1/d1)*(x1 + y1 * i + z1 * j + w1 * k) # b = (1/d2)*(x2 + y2 * i + z2 * j + w2 * k) # # we compute their sum as # # (a + b) = (1/d3)*(x3 + y3 * i + z3 * j + w3 * k) # # with d3 = d1 * d2 # x3 = d1 * x2 + d2 * x1 # y3 = d1 * y2 + d2 * y1 # z3 = d1 * z2 + d2 * z1 # w3 = d1 * w2 + d2 * w1 # # and then we reduce the sum by calling canonicalize().
# Note: We are assuming in this routine that the modulus is monic. This shouldn't # currently be an issue because it is impossible to create a number field with # a modulus that is not monic.
cpdef _sub_(self, _right): """ Subtract _right from self.
EXAMPLES::
sage: K.<a> = QQ[2^(1/3)]; Q.<i,j,k> = QuaternionAlgebra(K, -3, a) sage: z = a + i + (2/3)*a^3*j + (1+a)*k; w = a - i - (2/3)*a^3*j + (1/3+a)*k sage: type(z) <... 'sage.algebras.quatalg.quaternion_algebra_element.QuaternionAlgebraElement_number_field'> sage: z._sub_(w) 2*i + 8/3*j + 2/3*k """ # Implementation Note: To obtain _sub_, we simply replace every occurrence of # "add" in _add_ with "sub"; that is, we s/add/sub to get _sub_
# Note: We are assuming in this routine that the modulus is monic. This shouldn't # currently be an issue because it is impossible to create a number field with # a modulus that is not monic.
cpdef _mul_(self, _right): """ Multiply self and _right.
EXAMPLES::
sage: K.<a> = QQ[2^(1/3)]; Q.<i,j,k> = QuaternionAlgebra(K, -3, a) sage: z = a + i + (2/3)*a^3*j + (1+a)*k; w = a - i - (2/3)*a^3*j + (1/3+a)*k sage: type(z) <... 'sage.algebras.quatalg.quaternion_algebra_element.QuaternionAlgebraElement_number_field'> sage: z._mul_(w) 5*a^2 - 7/9*a + 9 + (-8/3*a^2 - 16/9*a)*i + (-6*a - 4)*j + (2*a^2 + 4/3*a)*k """ # We use the following formula for multiplication: # # Given two quaternion algebra elements # # a = (1/d1)*(x1 + y1 * i + z1 * j + w1 * k) # b = (1/d2)*(x2 + y2 * i + z2 * j + w2 * k) # # we compute their product as # # ab = (1/d3)*(x3 + y3 * i + z3 * j + w3 * k) # # where # x3 = t1 + a * t2 + b * (t3 - a*t4) # y3 = s1*(x2 + y2) - t1 - t2 + b*( s2*(z2 - w2) - t3 + t4) # z3 = t5 - a*t6 + t7 + a*t8 # w3 = (x2 - y2)*s2 - t5 + t6 + s1*(z2 + w2) - t7 - t8 # # and where # t1 = x1 * x2 # t2 = y1 * y2 # t3 = z1 * z2 # t4 = w1 * w2 # t5 = x2 * z1 # t6 = y2 * w1 # t7 = x1 * z2 # t8 = y1 * w2 # # s1 = x1 + y1 # s2 = z1 + w1 # # This takes more polynomial addition operations but fewer polynomial multiplication # operations than the "straightforward" multiplication method. # # There might be a way to optimize this formula further.
#------------------
#------------------
#------------------
#------------------
# At this point we have essentially computed the product, but we still # need to reduce modulo the modulus, which is what the following 12 lines do. # # When this was written, the version of flint in Sage had problems with # fpmz_poly_divrem(). This should be fixed in the newest version of # flint, which also should have some new functions which should do # this faster (Bill Hart sent an email to Bober and William about this). # # This should be fixed in the near future. (I don't know how much # faster it will be when it is updated, but the following code is # currently quite a bottleneck.
cdef inline canonicalize(self): """ Put the representation of this quaternion element into smallest form. For a = `(1/d)(x + yi + zj + wk)` we divide `a`, `x`, `y`, `z`, and `w` by the gcd of all of them.
TESTS::
sage: F = QQ[3^(1/3)] sage: a = F.gen() sage: K.<i,j,k> = QuaternionAlgebra(F, -10 + a, -7 - a) sage: ((1/4 + 1/2 * i + a^3/7 * j + a/28 * k)*14*i)^3 # implicit doctest 34503/2*a^2 + 132195/2*a + 791399/4 + (203/8*a^2 - 10591*a + 169225/4)*i + (-84695/4*a^2 + 483413/8*a + 18591/4)*j + (-87/2*a^2 + 18156*a - 72525)*k """
# Note: this function changes the module-level global variables # U1 and U2, so it isn't always safe to use this in the middle of # another function. Normally this function is called # at the end of an arithmetic routine, so this is fine.
# Implementation-wise, we compute the GCD's one at a time, # and quit if it ever becomes one
cdef fmpz_t content
####################################################################### # Versioned unpickle functions #######################################################################
def unpickle_QuaternionAlgebraElement_generic_v0(*args): """ EXAMPLES::
sage: K.<X> = QQ[] sage: Q.<i,j,k> = QuaternionAlgebra(Frac(K), -5,-19); z = 2/3 + i*X - X^2*j + X^3*k sage: f, t = z.__reduce__() sage: sage.algebras.quatalg.quaternion_algebra_element.unpickle_QuaternionAlgebraElement_generic_v0(*t) 2/3 + X*i + (-X^2)*j + X^3*k sage: sage.algebras.quatalg.quaternion_algebra_element.unpickle_QuaternionAlgebraElement_generic_v0(*t) == z True """
def unpickle_QuaternionAlgebraElement_rational_field_v0(*args): """ EXAMPLES::
sage: Q.<i,j,k> = QuaternionAlgebra(-5,-19); a = 2/3 + i*5/7 - j*2/5 +19/2 sage: f, t = a.__reduce__() sage: sage.algebras.quatalg.quaternion_algebra_element.unpickle_QuaternionAlgebraElement_rational_field_v0(*t) 61/6 + 5/7*i - 2/5*j """
def unpickle_QuaternionAlgebraElement_number_field_v0(*args): """ EXAMPLES::
sage: K.<a> = QQ[2^(1/3)]; Q.<i,j,k> = QuaternionAlgebra(K, -3, a); z = i + j sage: f, t = z.__reduce__() sage: sage.algebras.quatalg.quaternion_algebra_element.unpickle_QuaternionAlgebraElement_number_field_v0(*t) i + j sage: sage.algebras.quatalg.quaternion_algebra_element.unpickle_QuaternionAlgebraElement_number_field_v0(*t) == z True """ |