| 1 | """ |
|---|
| 2 | Quaternion algebras |
|---|
| 3 | |
|---|
| 4 | AUTHOR: David Kohel, 2005-09 |
|---|
| 5 | |
|---|
| 6 | TESTS: |
|---|
| 7 | sage: A = QuaternionAlgebra(QQ, -1,-1, names=list('ijk')) |
|---|
| 8 | sage: A == loads(dumps(A)) |
|---|
| 9 | True |
|---|
| 10 | sage: i, j, k = A.gens() |
|---|
| 11 | sage: i == loads(dumps(i)) |
|---|
| 12 | True |
|---|
| 13 | |
|---|
| 14 | """ |
|---|
| 15 | |
|---|
| 16 | #***************************************************************************** |
|---|
| 17 | # Copyright (C) 2005 David Kohel <kohel@maths.usyd.edu> |
|---|
| 18 | # |
|---|
| 19 | # Distributed under the terms of the GNU General Public License (GPL) |
|---|
| 20 | # |
|---|
| 21 | # This code is distributed in the hope that it will be useful, |
|---|
| 22 | # but WITHOUT ANY WARRANTY; without even the implied warranty |
|---|
| 23 | # of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. |
|---|
| 24 | # |
|---|
| 25 | # See the GNU General Public License for more details; the full text |
|---|
| 26 | # is available at: |
|---|
| 27 | # |
|---|
| 28 | # http://www.gnu.org/licenses/ |
|---|
| 29 | #***************************************************************************** |
|---|
| 30 | |
|---|
| 31 | from sage.misc.misc import mul |
|---|
| 32 | from sage.rings.arith import kronecker, GCD, hilbert_symbol |
|---|
| 33 | from sage.rings.integer import Integer |
|---|
| 34 | from sage.rings.all import (IntegerRing, RationalField, PolynomialRing, is_Field) |
|---|
| 35 | from sage.modules.free_module import FreeModule |
|---|
| 36 | from sage.modules.free_module import VectorSpace |
|---|
| 37 | from sage.matrix.matrix_space import MatrixSpace |
|---|
| 38 | from sage.algebras.free_algebra import FreeAlgebra |
|---|
| 39 | from sage.algebras.free_algebra_quotient import FreeAlgebraQuotient |
|---|
| 40 | from sage.algebras.algebra_element import AlgebraElement |
|---|
| 41 | from sage.algebras.free_algebra_quotient_element import FreeAlgebraQuotientElement |
|---|
| 42 | from sage.algebras.quaternion_algebra_element import QuaternionAlgebraElement, QuaternionAlgebraElement_fast |
|---|
| 43 | |
|---|
| 44 | import weakref |
|---|
| 45 | |
|---|
| 46 | def sign(x): |
|---|
| 47 | if x < 0: |
|---|
| 48 | return -1 |
|---|
| 49 | elif x > 0: |
|---|
| 50 | return 1 |
|---|
| 51 | else: |
|---|
| 52 | return 0 |
|---|
| 53 | |
|---|
| 54 | def fundamental_discriminant(D): |
|---|
| 55 | D = Integer(D) |
|---|
| 56 | D = D.square_free_part() |
|---|
| 57 | if D%4 in (0,1): |
|---|
| 58 | return D |
|---|
| 59 | return 4*D |
|---|
| 60 | |
|---|
| 61 | def ramified_primes(a,b): |
|---|
| 62 | a = Integer(a); b = Integer(b) |
|---|
| 63 | if a.is_square() or b.is_square() or (a+b).is_square(): |
|---|
| 64 | return [ ] |
|---|
| 65 | a = a.square_free_part() |
|---|
| 66 | b = b.square_free_part() |
|---|
| 67 | c = Integer(GCD(a,b)) |
|---|
| 68 | if c != 1: |
|---|
| 69 | p = c.factor()[0][0] |
|---|
| 70 | ram_prms = ramified_primes(p,-(b//p)) |
|---|
| 71 | for p in ramified_primes(a//p,b): |
|---|
| 72 | if p in ram_prms: |
|---|
| 73 | ram_prms.remove(p) |
|---|
| 74 | else: |
|---|
| 75 | ram_prms.append(p) |
|---|
| 76 | ram_prms.sort() |
|---|
| 77 | return ram_prms |
|---|
| 78 | ram_prms = [ ] |
|---|
| 79 | S1 = [ p[0] for p in abs(a).factor() ] |
|---|
| 80 | for p in S1: |
|---|
| 81 | if p == 2 and b%4 == 3: |
|---|
| 82 | if kronecker(a+b,p) == -1: |
|---|
| 83 | ram_prms.append(p) |
|---|
| 84 | elif kronecker(b,p) == -1: |
|---|
| 85 | ram_prms.append(p) |
|---|
| 86 | S2 = [ p[0] for p in abs(b).factor() ] |
|---|
| 87 | for q in S2: |
|---|
| 88 | if q == 2 and a%4 == 3: |
|---|
| 89 | if kronecker(a+b,q) == -1: |
|---|
| 90 | ram_prms.append(q) |
|---|
| 91 | elif kronecker(a,q) == -1: |
|---|
| 92 | ram_prms.append(q) |
|---|
| 93 | if not 2 in ram_prms and a%4 == 3 and b%4 == 3: |
|---|
| 94 | ram_prms.append(2) |
|---|
| 95 | ram_prms.sort() |
|---|
| 96 | return ram_prms |
|---|
| 97 | |
|---|
| 98 | def ramified_primes_from_discs(D1,D2,T): |
|---|
| 99 | M = Integer(GCD([D1,D2,T])) |
|---|
| 100 | D3 = (T**2 - D1*D2)//4 |
|---|
| 101 | facs = D3.factor() |
|---|
| 102 | D1 = fundamental_discriminant(D1) |
|---|
| 103 | D2 = fundamental_discriminant(D2) |
|---|
| 104 | D3 = fundamental_discriminant(D3) |
|---|
| 105 | ram_prms = [] |
|---|
| 106 | for pow in facs: |
|---|
| 107 | p = pow[0] |
|---|
| 108 | if pow[1]%2 == 1: |
|---|
| 109 | chi = (kronecker(D,p) for D in (D1,D2,D3)) |
|---|
| 110 | if -1 in chi: |
|---|
| 111 | ram_prms.append(p) |
|---|
| 112 | elif not 1 in chi and hilbert_symbol(D1,D3,p) == -1: |
|---|
| 113 | ram_prms.append(p) |
|---|
| 114 | elif D1%p == 0 and D2%p == 0: |
|---|
| 115 | chi = (kronecker(D,p) for D in (D1,D2,D3)) |
|---|
| 116 | if hilbert_symbol(D1,D3,p) == -1: |
|---|
| 117 | ram_prms.append(p) |
|---|
| 118 | return ram_prms |
|---|
| 119 | |
|---|
| 120 | _cache = {} |
|---|
| 121 | |
|---|
| 122 | def QuaternionAlgebra(K, a, b, names=['i','j','k'], denom=1): |
|---|
| 123 | """ |
|---|
| 124 | Return the quaternion algebra over $K$ generated by $i$, $j$, and $k$ |
|---|
| 125 | such that $i^2 = a$, $j^2 = b$, and $ij=-ji=k$. |
|---|
| 126 | |
|---|
| 127 | INPUT: |
|---|
| 128 | K -- field |
|---|
| 129 | a -- element of K |
|---|
| 130 | b -- element of K |
|---|
| 131 | names -- list of three strings |
|---|
| 132 | denom -- (optional, default 1) |
|---|
| 133 | |
|---|
| 134 | EXAMPLES: |
|---|
| 135 | sage: A.<i,j,k> = QuaternionAlgebra(QQ, -1,-1) |
|---|
| 136 | sage: i^2 |
|---|
| 137 | -1 |
|---|
| 138 | sage: j^2 |
|---|
| 139 | -1 |
|---|
| 140 | sage: i*j |
|---|
| 141 | k |
|---|
| 142 | sage: j*i |
|---|
| 143 | -k |
|---|
| 144 | sage: (i + j + k)^2 |
|---|
| 145 | -3 |
|---|
| 146 | sage: A.ramified_primes() |
|---|
| 147 | [2] |
|---|
| 148 | """ |
|---|
| 149 | if not is_Field(K): |
|---|
| 150 | raise ValueError, "Base ring K (= %s) must be a field."%K |
|---|
| 151 | |
|---|
| 152 | if K(2) == 0: |
|---|
| 153 | raise ValueError, "Base ring K (= %s) must be a field of characteristic different from 2."%K |
|---|
| 154 | |
|---|
| 155 | try: |
|---|
| 156 | a = K(a) |
|---|
| 157 | except TypeError: |
|---|
| 158 | raise ValueError, "Arguments a = %s and b = %s must coerce into K (= %s)."%(a,b,K) |
|---|
| 159 | try: |
|---|
| 160 | b = K(b) |
|---|
| 161 | except TypeError: |
|---|
| 162 | raise ValueError, "Arguments a = %s and b = %s must coerce into K (= %s)."%(a,b,K) |
|---|
| 163 | |
|---|
| 164 | if a == 0 or b == 0: |
|---|
| 165 | raise ValueError, "Arguments a = %s and b = %s must be nonzero."%(a,b) |
|---|
| 166 | |
|---|
| 167 | if denom == 0: |
|---|
| 168 | raise ValueError, "Argument denom (= %s) must be a nonzero."%denom |
|---|
| 169 | |
|---|
| 170 | if isinstance(names, list): |
|---|
| 171 | names = tuple(names) |
|---|
| 172 | |
|---|
| 173 | key = (K, a, b, names, denom) |
|---|
| 174 | global _cache |
|---|
| 175 | if _cache.has_key(key): |
|---|
| 176 | A = _cache[key]() |
|---|
| 177 | if not A is None: |
|---|
| 178 | return A |
|---|
| 179 | |
|---|
| 180 | if K is RationalField(): |
|---|
| 181 | prms = ramified_primes(a,b) |
|---|
| 182 | H = QuaternionAlgebra_generic(K, [2,0,0,0], prms) |
|---|
| 183 | else: |
|---|
| 184 | H = QuaternionAlgebra_generic(K, [2,0,0,0]) |
|---|
| 185 | A = FreeAlgebra(K,3, names=names) |
|---|
| 186 | F = A.monoid() |
|---|
| 187 | mons = [ F(1) ] + [ F.gen(i) for i in range(3) ] |
|---|
| 188 | M = MatrixSpace(K,4) |
|---|
| 189 | m = denom |
|---|
| 190 | c = a/m |
|---|
| 191 | d = b/m |
|---|
| 192 | mats = [ |
|---|
| 193 | M([0,1,0,0, a,0,0,0, 0,0,0,-m, 0,0,-c,0]), |
|---|
| 194 | M([0,0,1,0, 0,0,0,m, b,0,0,0, 0,d,0,0]), |
|---|
| 195 | M([0,0,0,1, 0,0,c,0, 0,-d,0,0, -c*d,0,0,0]) ] |
|---|
| 196 | FreeAlgebraQuotient.__init__(H, A, mons=mons, mats=mats, names=names) |
|---|
| 197 | _cache[key] = weakref.ref(H) |
|---|
| 198 | return H |
|---|
| 199 | |
|---|
| 200 | def QuaternionAlgebraWithInnerProduct(K, norms, traces, names): |
|---|
| 201 | """ |
|---|
| 202 | """ |
|---|
| 203 | (n1,n2,n3) = norms |
|---|
| 204 | (t1,t2,t3,t12,t13,t23) = traces |
|---|
| 205 | T = t1*t23 + t2*t13 - t3*t12 |
|---|
| 206 | N = (t1*t2 - t12)*t13*t23 \ |
|---|
| 207 | + t23*(t23 - t2*t3)*n1 \ |
|---|
| 208 | + t13*(t13 - t1*t3)*n2 \ |
|---|
| 209 | + t12*(t12 - t1*t2)*n3 \ |
|---|
| 210 | + t1**2*n2*n3 + t2**2*n1*n3 + t3**2*n1*n2 - 4*n1*n2*n3 |
|---|
| 211 | if True and K(2).is_unit(): |
|---|
| 212 | D = T**2 - 4*N |
|---|
| 213 | try: |
|---|
| 214 | S = D.square_root() |
|---|
| 215 | except ArithmeticError: |
|---|
| 216 | raise ValueError, "Invalid inner product input." |
|---|
| 217 | assert bool |
|---|
| 218 | x = (T + S)/2 |
|---|
| 219 | else: |
|---|
| 220 | # In characteristic 2 we can't diagonalize the quadratic |
|---|
| 221 | # form so we solve for its roots. |
|---|
| 222 | X = PolynomialRing(K).gen() |
|---|
| 223 | try: |
|---|
| 224 | x = (X**2 - T*X + N).roots()[0][0] |
|---|
| 225 | except IndexError: |
|---|
| 226 | raise ValueError, "Invalid inner product input." |
|---|
| 227 | assert (x**2 - T*x + N) == 0 |
|---|
| 228 | M = MatrixSpace(K,5) |
|---|
| 229 | m = M([ [ 2, t1, t2, t3, t1*t2-t12 ], |
|---|
| 230 | [ t1, 2*n1, t12, t13, t2*n1 ], |
|---|
| 231 | [ t2, t12, 2*n2, t23, t1*n2 ], |
|---|
| 232 | [ t3, t13, t23, 2*n3, x ], |
|---|
| 233 | [ t1*t2-t12, t2*n1, t1*n2, x, 2*n1*n2] ]) |
|---|
| 234 | v = m.kernel().gen(0) |
|---|
| 235 | r4 = -1/v[4] |
|---|
| 236 | V = VectorSpace(K,4) |
|---|
| 237 | vij = V([ r4*v[i] for i in range(4) ]) |
|---|
| 238 | (s0,s1,s2,s3) = vij.list() |
|---|
| 239 | r3 = 1/s3 |
|---|
| 240 | vik = r3 * (V([ s1*n1, -(s0+s1*t1), -n1, 0 ]) + (t1-s2)*vij) |
|---|
| 241 | vkj = r3 * (V([ s2*n2, -n2, -(s0+s2*t2), 0 ]) + (t2-s1)*vij) |
|---|
| 242 | vji = V([ -t12, t2, t1, 0 ]) - vij |
|---|
| 243 | vki = V([ -t13, t3, 0, t1 ]) - vik |
|---|
| 244 | vjk = V([ -t23, 0, t3, t2 ]) - vkj |
|---|
| 245 | H = QuaternionAlgebra_generic(K, [2,t1,t2,t3]) |
|---|
| 246 | A = FreeAlgebra(K,3, names=names) |
|---|
| 247 | F = A.monoid() |
|---|
| 248 | mons = [ F(1) ] + [ F.gen(i) for i in range(3) ] |
|---|
| 249 | M = MatrixSpace(K,4) |
|---|
| 250 | mi = M([ [0,1,0,0], [-n1,t1,0,0], vji.list(), vki.list() ]) |
|---|
| 251 | mj = M([ [0,0,1,0], vij.list(), [-n2,0,t2,0], vkj.list() ]) |
|---|
| 252 | mk = M([ [0,0,0,1], vik.list(), vjk.list(), [-n3,0,0,t3] ]) |
|---|
| 253 | mats = [mi,mj,mk] |
|---|
| 254 | FreeAlgebraQuotient.__init__(H, A, mons=mons, mats=mats, names=names) |
|---|
| 255 | return H |
|---|
| 256 | |
|---|
| 257 | def QuaternionAlgebraWithGramMatrix(K, gram, names): |
|---|
| 258 | """ |
|---|
| 259 | """ |
|---|
| 260 | if not isinstance(gram,Matrix) or not gram.is_symmetric: |
|---|
| 261 | raise AttributeError, "Argument gram (= %s) must be a symmetric matrix"%gram |
|---|
| 262 | (q0,t01,t02,t03,_,q1,t12,t13,_,_,q3,t23,_,_,_,q4) = gram.list() |
|---|
| 263 | n1 = q1/2 |
|---|
| 264 | n2 = q2/2 |
|---|
| 265 | n3 = q3/2 |
|---|
| 266 | return QuaternionAlgebraWithInnerProduct(K,norms,traces,names=names) |
|---|
| 267 | |
|---|
| 268 | def QuaternionAlgebraWithDiscriminants(D1, D2, T, names=['i','j','k'], M=2): |
|---|
| 269 | r""" |
|---|
| 270 | Return the quaternion algebra over the rationals generated by $i$, |
|---|
| 271 | $j$, and $k = (ij - ji)/M$ where $\Z[i]$, $\Z[j]$, and $\Z[k]$ are |
|---|
| 272 | quadratic suborders of discriminants $D_1$, $D_2$, and $D_3 = (D_1 |
|---|
| 273 | D_2 - T^2)/M^2$, respectively. The traces of $i$ and $j$ are |
|---|
| 274 | chosen in $\{0,1\}$. |
|---|
| 275 | |
|---|
| 276 | The integers $D_1$, $D_2$ and $T$ must all be even or all odd, and |
|---|
| 277 | $D_1$, $D_2$ and $D_3$ must each be the discriminant of some |
|---|
| 278 | quadratic order, i.e. nonsquare integers = 0, 1 (mod 4). |
|---|
| 279 | |
|---|
| 280 | INPUT: |
|---|
| 281 | D1 -- Integer |
|---|
| 282 | D2 -- Integer |
|---|
| 283 | T -- Integer |
|---|
| 284 | M -- Integer (default: 2) |
|---|
| 285 | |
|---|
| 286 | OUTPUT: |
|---|
| 287 | A quaternion algebra. |
|---|
| 288 | |
|---|
| 289 | EXAMPLES: |
|---|
| 290 | sage: A = QuaternionAlgebraWithDiscriminants(-7,-47,1, names=['i','j','k']) |
|---|
| 291 | sage: print A |
|---|
| 292 | Quaternion algebra with generators (i, j, k) over Rational Field |
|---|
| 293 | sage: i, j, k = A.gens() |
|---|
| 294 | sage: i**2 |
|---|
| 295 | -2 + i |
|---|
| 296 | sage: j**2 |
|---|
| 297 | -12 + j |
|---|
| 298 | sage: k**2 |
|---|
| 299 | -24 + k |
|---|
| 300 | sage: i.minimal_polynomial('x') |
|---|
| 301 | x^2 - x + 2 |
|---|
| 302 | sage: j.minimal_polynomial('x') |
|---|
| 303 | x^2 - x + 12 |
|---|
| 304 | """ |
|---|
| 305 | QQ = RationalField() |
|---|
| 306 | ZZ = IntegerRing() |
|---|
| 307 | if M != 2: |
|---|
| 308 | raise ValueError, "Not implemented: Argument denom (= %s) must be 2."%M |
|---|
| 309 | # adapt types of input arguments |
|---|
| 310 | D1 = ZZ(D1); D2 = ZZ(D2); T = ZZ(T) |
|---|
| 311 | if (D1*D2)%M**2-(T**2)%M**2 != 0: |
|---|
| 312 | raise ArithmeticError, \ |
|---|
| 313 | "Each of (D1 D2 - T^2) = %s must be 0 mod M^2 = %s"%(D3,M**2) |
|---|
| 314 | # Check that the D_i are 0 or 1 mod 4 and non-square: |
|---|
| 315 | if D1.is_square() or D2.is_square(): |
|---|
| 316 | raise ArithmeticError, "D1 (=%s) and D2 (=%s) must be nonsquare."%(D1,D2) |
|---|
| 317 | t1 = D1%4; t2 = D2%4 |
|---|
| 318 | if t1 in [2,3] or t2 in [2,3]: |
|---|
| 319 | raise ArithmeticError, "D1 (=%s) and D2 (=%s) must be in {0,1} mod 4."%(D1,D2) |
|---|
| 320 | n1 = (t1-D1)//4; n2 = (t2-D2)//4 |
|---|
| 321 | t12 = (t1*t2 - T)//2 |
|---|
| 322 | A = FreeAlgebra(RationalField(),3,names=names) |
|---|
| 323 | i, j, k = A.monoid().gens() |
|---|
| 324 | # Right matrix action on algebra: |
|---|
| 325 | MQ = MatrixSpace(QQ,4) |
|---|
| 326 | mi = MQ([0,1,0,0, -n1,t1,0,0, -t12,t2,t1,-1, -n1*t2,t1*t2-t12,n1,0]) |
|---|
| 327 | mj = MQ([0,0,1,0, 0,0,0,1, -n2,0,t2,0, 0,-n2,0,t2]) |
|---|
| 328 | # N.B. mk = mi*mj |
|---|
| 329 | t3 = t1*t2-t12 |
|---|
| 330 | mk = MQ([0,0,0,1, 0,0,-n1,t1, -n2*t1,n2,t3,0, -n1*n2,0,0,t3]) |
|---|
| 331 | mats = [ mi, mj, mk ] |
|---|
| 332 | prms = ramified_primes_from_discs(D1,D2,T) |
|---|
| 333 | H = QuaternionAlgebra_generic(QQ, [2,t1,t2,t3], prms) |
|---|
| 334 | A = FreeAlgebra(QQ,3, names=names) |
|---|
| 335 | F = A.monoid() |
|---|
| 336 | mons = [ F(1) ] + [ F.gen(i) for i in range(3) ] |
|---|
| 337 | FreeAlgebraQuotient.__init__(H, A, mons=mons, mats=mats, names=names) |
|---|
| 338 | return H |
|---|
| 339 | |
|---|
| 340 | class QuaternionAlgebra_generic(FreeAlgebraQuotient): |
|---|
| 341 | |
|---|
| 342 | def __init__(self, K, basis_traces = None, ramified_primes=None): |
|---|
| 343 | """ |
|---|
| 344 | """ |
|---|
| 345 | if ramified_primes != None: |
|---|
| 346 | self._ramified_primes = ramified_primes |
|---|
| 347 | if basis_traces != None: |
|---|
| 348 | self._basis_traces = basis_traces |
|---|
| 349 | |
|---|
| 350 | def __call__(self, x): |
|---|
| 351 | if hasattr(self, 'discriminants'): |
|---|
| 352 | if isinstance(x, QuaternionAlgebraElement_fast) and x.parent() is self: |
|---|
| 353 | return x |
|---|
| 354 | return QuaternionAlgebraElement_fast(self,x) |
|---|
| 355 | else: |
|---|
| 356 | if isinstance(x, QuaternionAlgebraElement) and x.parent() is self: |
|---|
| 357 | return x |
|---|
| 358 | return QuaternionAlgebraElement(self,x) |
|---|
| 359 | |
|---|
| 360 | def __repr__(self): |
|---|
| 361 | return "Quaternion algebra with generators %s over %s"%( |
|---|
| 362 | self.gens(), self.base_ring()) |
|---|
| 363 | |
|---|
| 364 | def gen(self,i): |
|---|
| 365 | """ |
|---|
| 366 | The i-th generator of the quaternion algebra. |
|---|
| 367 | """ |
|---|
| 368 | if i < 0 or not i < 4: |
|---|
| 369 | raise IndexError, \ |
|---|
| 370 | "Argument i (= %s) must be between 0 and %s."%(i, 3) |
|---|
| 371 | K = self.base_ring() |
|---|
| 372 | F = self.free_algebra().monoid() |
|---|
| 373 | return QuaternionAlgebraElement(self,{F.gen(i):K(1)}) |
|---|
| 374 | |
|---|
| 375 | def basis(self): |
|---|
| 376 | return (self(1), self([0,1,0,0]), self([0,0,1,0]), self([0,0,0,1])) |
|---|
| 377 | |
|---|
| 378 | def discriminant(self): |
|---|
| 379 | return self.gram_matrix().determinant() |
|---|
| 380 | |
|---|
| 381 | def gram_matrix(self): |
|---|
| 382 | """ |
|---|
| 383 | The Gram matrix of the inner product determined by the norm. |
|---|
| 384 | """ |
|---|
| 385 | try: |
|---|
| 386 | M = self.__vector_space.inner_product_matrix() |
|---|
| 387 | except: |
|---|
| 388 | K = self.base_ring() |
|---|
| 389 | M = MatrixSpace(K,4)(0) |
|---|
| 390 | B = self.basis() |
|---|
| 391 | for i in range(4): |
|---|
| 392 | x = B[i] |
|---|
| 393 | M[i,i] = 2*(x.reduced_norm()) |
|---|
| 394 | for j in range(i+1,4): |
|---|
| 395 | y = B[j] |
|---|
| 396 | c = (x * y.conjugate()).reduced_trace() |
|---|
| 397 | M[i,j] = c |
|---|
| 398 | M[j,i] = c |
|---|
| 399 | self.__vector_space = VectorSpace(K,4,inner_product_matrix = M) |
|---|
| 400 | return M |
|---|
| 401 | |
|---|
| 402 | def inner_product_matrix(self): |
|---|
| 403 | return self.gram_matrix() |
|---|
| 404 | |
|---|
| 405 | def is_commutative(self): |
|---|
| 406 | """ |
|---|
| 407 | EXAMPLES: |
|---|
| 408 | sage: Q.<i,j,k> = QuaternionAlgebra(QQ, -3,-7) |
|---|
| 409 | sage: Q.is_commutative() |
|---|
| 410 | False |
|---|
| 411 | """ |
|---|
| 412 | return False |
|---|
| 413 | |
|---|
| 414 | def ramified_primes(self): |
|---|
| 415 | try: |
|---|
| 416 | return self._ramified_primes |
|---|
| 417 | except: |
|---|
| 418 | raise AttributeError, "Ramified primes have not been computed." |
|---|
| 419 | |
|---|
| 420 | def random_element(self): |
|---|
| 421 | K = self.base_ring() |
|---|
| 422 | return self([ K.random_element() for _ in range(4) ]) |
|---|
| 423 | |
|---|
| 424 | def vector_space(self): |
|---|
| 425 | try: |
|---|
| 426 | V = self.__vector_space |
|---|
| 427 | except: |
|---|
| 428 | M = self.gram_matrix() # induces construction of V |
|---|
| 429 | V = self.__vector_space |
|---|
| 430 | return V |
|---|
| 431 | |
|---|
| 432 | |
|---|
| 433 | def QuaternionAlgebra_fast(K, a, b, names="ijk"): |
|---|
| 434 | H = QuaternionAlgebra(K, a, b, names) |
|---|
| 435 | H.discriminants = (a,b) |
|---|
| 436 | return H |
|---|
| 437 | |
|---|
| 438 | class QuaternionAlgebra_faster(QuaternionAlgebra_generic): |
|---|
| 439 | |
|---|
| 440 | def __call__(self, x): |
|---|
| 441 | if isinstance(x, QuaternionAlgebraElement_fast) and x.parent() is self: |
|---|
| 442 | return x |
|---|
| 443 | return QuaternionAlgebraElement_fast(self,x) |
|---|