# HG changeset patch
# User Nick Alexander <ncalexander@gmail.com>
# Date 1205105455 25200
# Node ID bc0607c6ba93ac3df95f7c1fda1119eb8fb6c29e
# Parent f801a96d09ceaca6304a8065c1547a641ba3765f
#2448: clean code, add docstrings to quadratic_forms/binary_qf.py
diff -r f801a96d09ce -r bc0607c6ba93 sage/quadratic_forms/binary_qf.py
|
a
|
b
|
|
| 1 | 1 | """ |
| 2 | | Binary Quadratic Forms |
| | 2 | Binary Quadratic Forms with integer coefficients. |
| | 3 | |
| | 4 | The form $a x^2 + b x y + d y^2$ is stored as a triple (a, b, c) of integers. |
| | 5 | |
| | 6 | EXAMPLES: |
| | 7 | sage: Q = BinaryQF([1,2,3]) |
| | 8 | sage: Q |
| | 9 | x^2 + 2*x*y + 3*y^2 |
| | 10 | sage: Q.discriminant() |
| | 11 | -8 |
| | 12 | sage: Q.reduce() |
| | 13 | x^2 + 2*y^2 |
| | 14 | sage: Q(1, 1) |
| | 15 | 6 |
| | 16 | |
| | 17 | TESTS: |
| | 18 | sage: Q == loads(dumps(Q)) |
| | 19 | True |
| 3 | 20 | |
| 4 | 21 | AUTHORS: |
| 5 | 22 | -- Jon Hanke (2006-08-08): |
| 6 | 23 | * Appended to add the reduced_representatives, dyadic_trace, |
| 7 | 24 | is_reduced, and + on 8-3-2006 for Coding Sprint #2. |
| 8 | 25 | * Added Documentation and complex_point() method on 8-8-2006. |
| 9 | | |
| 10 | | NOTE: This code is mainly inspired by an application to computing |
| 11 | | Siegel modular forms. |
| | 26 | -- Nick Alexander: add doctests and clean code for Doc Days 2 |
| 12 | 27 | """ |
| 13 | 28 | |
| 14 | 29 | #***************************************************************************** |
| … |
… |
class BinaryQF(SageObject): |
| 45 | 60 | sage: b.discriminant() |
| 46 | 61 | -8 |
| 47 | 62 | """ |
| 48 | | ## Initializes the form with a 3-element list |
| | 63 | # Initializes the form with a 3-element list |
| 49 | 64 | def __init__(self, abc_triple): |
| 50 | 65 | """ |
| 51 | 66 | Creates the binary quadratic form $ax^2 + bxy + cy^2$ from the |
| … |
… |
class BinaryQF(SageObject): |
| 54 | 69 | EXAMPLES: |
| 55 | 70 | sage: Q = BinaryQF([1,2,3]) |
| 56 | 71 | sage: Q |
| 57 | | x^2 + 2xy + 3y^2 |
| | 72 | x^2 + 2*x*y + 3*y^2 |
| | 73 | sage: Q = BinaryQF([1,2]) |
| | 74 | Traceback (most recent call last): |
| | 75 | ... |
| | 76 | ValueError: Binary quadratic form must be given by a list of three coefficients |
| 58 | 77 | """ |
| 59 | | assert len(abc_triple) == 3 ## Check we have three coefficients |
| | 78 | if len(abc_triple) != 3: |
| | 79 | # Check we have three coefficients |
| | 80 | raise ValueError, "Binary quadratic form must be given by a list of three coefficients" |
| 60 | 81 | self.a = ZZ(abc_triple[0]) |
| 61 | 82 | self.b = ZZ(abc_triple[1]) |
| 62 | 83 | self.c = ZZ(abc_triple[2]) |
| | 84 | self._reduced_form = None |
| 63 | 85 | |
| 64 | 86 | def __getitem__(self, n): |
| 65 | 87 | """ |
| | 88 | Return the n-th component of this quadratic form. |
| | 89 | |
| | 90 | If this form is $a x^2 + b x y + c y^2$, the 0-th component is $a$, |
| | 91 | the 1-st component is $b$, and $2$-nd component is $c$. |
| | 92 | |
| | 93 | Indexing is like lists -- negative indices and slices are allowed. |
| | 94 | |
| 66 | 95 | EXAMPLES: |
| 67 | 96 | sage: Q = BinaryQF([2,3,4]) |
| 68 | 97 | sage: Q[0] |
| 69 | 98 | 2 |
| 70 | 99 | sage: Q[2] |
| 71 | 100 | 4 |
| | 101 | sage: Q[:2] |
| | 102 | (2, 3) |
| 72 | 103 | sage: tuple(Q) |
| 73 | 104 | (2, 3, 4) |
| 74 | 105 | sage: list(Q) |
| 75 | 106 | [2, 3, 4] |
| 76 | 107 | """ |
| 77 | | if n == 0: |
| 78 | | return self.a |
| 79 | | elif n == 1: |
| 80 | | return self.b |
| 81 | | elif n == 2: |
| 82 | | return self.c |
| 83 | | else: |
| 84 | | raise IndexError |
| | 108 | return (self.a, self.b, self.c)[n] |
| | 109 | |
| | 110 | def __call__(self, *args): |
| | 111 | r""" |
| | 112 | Evaluate this quadratic form at a point. |
| | 113 | |
| | 114 | INPUT: |
| | 115 | args -- x and y values, often as a pair x, y or a list [x, y] |
| | 116 | |
| | 117 | EXAMPLES: |
| | 118 | sage: Q = BinaryQF([2,3,4]) |
| | 119 | sage: Q(1, 2) |
| | 120 | 24 |
| | 121 | """ |
| | 122 | return self.polynomial()(*args) |
| 85 | 123 | |
| 86 | 124 | def __cmp__(self, right): |
| | 125 | """ |
| | 126 | Returns True if self and right are identical: the same coefficients. |
| | 127 | |
| | 128 | EXAMPLES: |
| | 129 | sage: P = BinaryQF([2,2,3]) |
| | 130 | sage: Q = BinaryQF([2,2,3]) |
| | 131 | sage: R = BinaryQF([1,2,3]) |
| | 132 | sage: P == Q # indirect doctest |
| | 133 | True |
| | 134 | sage: P == R # indirect doctest |
| | 135 | False |
| | 136 | |
| | 137 | TESTS: |
| | 138 | sage: P == P |
| | 139 | True |
| | 140 | sage: Q == P |
| | 141 | True |
| | 142 | sage: R == P |
| | 143 | False |
| | 144 | sage: P == 2 |
| | 145 | False |
| | 146 | """ |
| 87 | 147 | if not isinstance(right, BinaryQF): |
| 88 | 148 | return cmp(type(self), type(right)) |
| 89 | 149 | return cmp((self.a,self.b,self.c), (right.a,right.b,right.c)) |
| 90 | 150 | |
| 91 | 151 | def __add__(self, Q): |
| 92 | 152 | """ |
| 93 | | Returns the sum of the two forms (componentwise). |
| | 153 | Returns the component-wise sum of two forms. |
| | 154 | |
| | 155 | That is, given $a_1 x^2 + b_1 x y + c_1 y^2$ and $a_2 x^2 + b_2 x y + |
| | 156 | c_2 y^2$, returns the form |
| | 157 | $$(a_1 + a_2) x^2 + (b_1 + b_2) x y + (c_1 + c_2) y^2 .$$ |
| | 158 | |
| | 159 | EXAMPLES: |
| | 160 | sage: P = BinaryQF([2,2,3]); P |
| | 161 | 2*x^2 + 2*x*y + 3*y^2 |
| | 162 | sage: Q = BinaryQF([-1,2,2]); Q |
| | 163 | -1*x^2 + 2*x*y + 2*y^2 |
| | 164 | sage: P + Q |
| | 165 | x^2 + 4*x*y + 5*y^2 |
| | 166 | sage: P + Q == BinaryQF([1,4,5]) # indirect doctest |
| | 167 | True |
| | 168 | |
| | 169 | TESTS: |
| | 170 | sage: Q + P == BinaryQF([1,4,5]) # indirect doctest |
| | 171 | True |
| 94 | 172 | """ |
| 95 | 173 | return BinaryQF([self.a + Q.a, self.b + Q.b, self.c + Q.c]) |
| 96 | 174 | |
| | 175 | def __sub__(self, Q): |
| | 176 | """ |
| | 177 | Returns the component-wise difference of two forms. |
| | 178 | |
| | 179 | That is, given $a_1 x^2 + b_1 x y + c_1 y^2$ and $a_2 x^2 + b_2 x y + |
| | 180 | c_2 y^2$, returns the form |
| | 181 | $$(a_1 - a_2) x^2 + (b_1 - b_2) x y + (c_1 - c_2) y^2 .$$ |
| | 182 | |
| | 183 | EXAMPLES: |
| | 184 | sage: P = BinaryQF([2,2,3]); P |
| | 185 | 2*x^2 + 2*x*y + 3*y^2 |
| | 186 | sage: Q = BinaryQF([-1,2,2]); Q |
| | 187 | -1*x^2 + 2*x*y + 2*y^2 |
| | 188 | sage: P - Q |
| | 189 | 3*x^2 + y^2 |
| | 190 | sage: P - Q == BinaryQF([3,0,1]) # indirect doctest |
| | 191 | True |
| | 192 | |
| | 193 | TESTS: |
| | 194 | sage: Q - P == BinaryQF([3,0,1]) # indirect doctest |
| | 195 | False |
| | 196 | sage: Q - P != BinaryQF([3,0,1]) # indirect doctest |
| | 197 | True |
| | 198 | """ |
| | 199 | return BinaryQF([self.a - Q.a, self.b - Q.b, self.c - Q.c]) |
| 97 | 200 | |
| 98 | 201 | def _repr_(self): |
| 99 | 202 | """ |
| 100 | 203 | Display the quadratic form. |
| | 204 | |
| | 205 | EXAMPLES: |
| | 206 | sage: Q = BinaryQF([1,2,3]); Q # indirect doctest |
| | 207 | x^2 + 2*x*y + 3*y^2 |
| | 208 | |
| | 209 | sage: Q = BinaryQF([-1,2,3]); Q |
| | 210 | -1*x^2 + 2*x*y + 3*y^2 |
| | 211 | |
| | 212 | sage: Q = BinaryQF([0,0,0]); Q |
| | 213 | 0 |
| 101 | 214 | """ |
| 102 | | ## Deal with the zero form |
| 103 | | if self.a==0 and self.b==0 and self.c==0: |
| 104 | | return "0" |
| 105 | | |
| 106 | | ## Account for leading coeff |
| 107 | | lc_flag = True |
| 108 | | |
| 109 | | ## Print the first coefficient |
| 110 | | out_str = "" |
| 111 | | if abs(self.a) > 1: |
| 112 | | out_str += str(self.a) |
| 113 | | if self.a != 0: |
| 114 | | out_str += "x^2 " |
| 115 | | lc_flag = False |
| 116 | | |
| 117 | | ## Print the second coefficient |
| 118 | | if self.b < 0: |
| 119 | | out_str += " - " |
| 120 | | elif self.b > 0 and lc_flag == False: |
| 121 | | out_str += " + " |
| 122 | | if abs(self.b) > 1: |
| 123 | | out_str += str(abs(self.b)) |
| 124 | | if self.b != 0: |
| 125 | | out_str += "xy " |
| 126 | | lc_flag = False |
| 127 | | |
| 128 | | ## Print the third coefficient |
| 129 | | if self.c < 0: |
| 130 | | out_str += " - " |
| 131 | | elif self.c > 0 and lc_flag == False: |
| 132 | | out_str += " + " |
| 133 | | if abs(self.c) > 1: |
| 134 | | out_str += str(abs(self.c)) |
| 135 | | if self.c != 0: |
| 136 | | out_str += "y^2 " |
| 137 | | lc_flag = False |
| 138 | | |
| 139 | | return out_str |
| 140 | | |
| | 215 | return repr(self.polynomial()) |
| 141 | 216 | |
| 142 | 217 | def polynomial(self): |
| 143 | 218 | """ |
| … |
… |
class BinaryQF(SageObject): |
| 148 | 223 | sage: Q = BinaryQF([1,2,3]) |
| 149 | 224 | sage: Q.polynomial() |
| 150 | 225 | x^2 + 2*x*y + 3*y^2 |
| | 226 | |
| | 227 | sage: Q = BinaryQF([-1,-2,3]) |
| | 228 | sage: Q.polynomial() |
| | 229 | -1*x^2 - 2*x*y + 3*y^2 |
| | 230 | |
| | 231 | sage: Q = BinaryQF([0,0,0]) |
| | 232 | sage: Q.polynomial() |
| | 233 | 0 |
| 151 | 234 | """ |
| 152 | 235 | M = ZZ['x,y'] |
| 153 | 236 | (x,y) = M.gens() |
| 154 | 237 | return self.a * x**2 + self.b* x*y + self.c * y**2 |
| 155 | | |
| 156 | | def dyadic_trace(self): |
| 157 | | """ |
| 158 | | Returns the"dyadic trace" of $ac - b^2$ of the |
| 159 | | binary form $ax^2 + bxy + cy^2$. |
| 160 | | |
| 161 | | EXAMPLES: |
| 162 | | sage: Q = BinaryQF([1,2,3]) |
| 163 | | sage: Q.dyadic_trace() |
| 164 | | 2 |
| 165 | | |
| 166 | | WARNING: Hopefully this is correct?!? =) |
| 167 | | """ |
| 168 | | return self.a + self.c - self.b |
| 169 | | |
| 170 | 238 | |
| 171 | 239 | def discriminant(self): |
| 172 | 240 | """ |
| … |
… |
class BinaryQF(SageObject): |
| 231 | 299 | sage: a.is_reduced() |
| 232 | 300 | False |
| 233 | 301 | sage: b = a.reduce(); b |
| 234 | | x^2 + xy + 2y^2 |
| | 302 | x^2 + x*y + 2*y^2 |
| 235 | 303 | sage: b.is_reduced() |
| 236 | 304 | True |
| 237 | 305 | """ |
| 238 | | v = eval(repr(pari('Vec(qfbred(Qfb(%s,%s,%s)))'%(self.a,self.b,self.c)))) |
| 239 | | return BinaryQF(v) |
| | 306 | if self._reduced_form is None: |
| | 307 | v = list(pari('Vec(qfbred(Qfb(%s,%s,%s)))'%(self.a,self.b,self.c))) |
| | 308 | self._reduced_form = BinaryQF(v) |
| | 309 | return self._reduced_form |
| 240 | 310 | |
| 241 | 311 | def is_reduced(self): |
| 242 | 312 | """ |
| … |
… |
class BinaryQF(SageObject): |
| 262 | 332 | True |
| 263 | 333 | """ |
| 264 | 334 | return self.reduce() == self |
| 265 | | #if self.discriminant() >= 0: |
| 266 | | # raise NotImplementedError, "discriminant must be negative (for now)" |
| 267 | | |
| 268 | | ## Check that the form is weakly reduced |
| 269 | | #if self.is_weakly_reduced() == False: |
| 270 | | # return False |
| 271 | | |
| 272 | | ## Check that b >= 0 when a = |b| or c = |b|, since it's weakly reduced |
| 273 | | #if self.b >= 0: |
| 274 | | # return True |
| 275 | | #else: |
| 276 | | # return (self.a != abs(self.b)) and (self.c != abs(self.b)) |
| 277 | | |
| 278 | 335 | |
| 279 | 336 | def complex_point(self): |
| 280 | 337 | """ |
| 281 | 338 | Returns the point in the complex upper half-plane associated |
| 282 | 339 | to this (positive definite) quadratic form). |
| | 340 | |
| | 341 | For positive definite forms with negative discriminants, this is a |
| | 342 | root $\tau$ of $a x^2 + b x + c$ with the imaginary part of $\tau$ |
| | 343 | greater than 0. |
| 283 | 344 | |
| 284 | 345 | EXAMPLES: |
| 285 | 346 | sage: Q = BinaryQF([1,0,1]) |
| … |
… |
class BinaryQF(SageObject): |
| 296 | 357 | |
| 297 | 358 | def BinaryQF_reduced_representatives(D): |
| 298 | 359 | """ |
| 299 | | Returns inequivalent reduced representatives for the equivalence |
| | 360 | Returns a list of inequivalent reduced representatives for the equivalence |
| 300 | 361 | classes of positive definite binary forms of discriminant D. |
| 301 | 362 | |
| 302 | | Note: These representatives are not necessarily primitive, unless |
| 303 | | the discriminant is fundamental! |
| | 363 | NOTE: The order of the representatives is unspecified but deterministic. |
| | 364 | |
| | 365 | WARNING: The representatives are not necessarily primitive, unless the |
| | 366 | discriminant is fundamental! |
| 304 | 367 | |
| 305 | 368 | EXAMPLES: |
| 306 | 369 | sage: BinaryQF_reduced_representatives(-4) |
| 307 | | [x^2 + y^2 ] |
| | 370 | [x^2 + y^2] |
| 308 | 371 | |
| 309 | 372 | sage: BinaryQF_reduced_representatives(-163) |
| 310 | | [x^2 + xy + 41y^2 ] |
| | 373 | [x^2 + x*y + 41*y^2] |
| 311 | 374 | |
| 312 | 375 | sage: BinaryQF_reduced_representatives(-12) |
| 313 | | [x^2 + 3y^2 , 2x^2 + 2xy + 2y^2 ] |
| | 376 | [x^2 + 3*y^2, 2*x^2 + 2*x*y + 2*y^2] |
| 314 | 377 | |
| 315 | 378 | sage: BinaryQF_reduced_representatives(-16) |
| 316 | | [x^2 + 4y^2 , 2x^2 + 2y^2 ] |
| | 379 | [x^2 + 4*y^2, 2*x^2 + 2*y^2] |
| | 380 | |
| | 381 | The number of inequivalent reduced binary forms with a fixed negative |
| | 382 | fundamental discriminant D is the class number of the quadratic field |
| | 383 | $Q(\sqrt{D})$. |
| | 384 | |
| | 385 | sage: len(BinaryQF_reduced_representatives(-13*4)) |
| | 386 | 2 |
| | 387 | sage: QuadraticField(-13*4, 'a').class_number() |
| | 388 | 2 |
| 317 | 389 | """ |
| 318 | 390 | if not ( D < 0 and D in ZZ and ((D % ZZ(4) == 0) or (D % ZZ(4) == 1))): |
| 319 | 391 | raise ValueError, "discriminant is not valid and positive definite" |
| 320 | 392 | |
| 321 | | ## Find the range of allowed b's |
| | 393 | # Find the range of allowed b's |
| 322 | 394 | bmax = (-D / ZZ(3)).sqrt_approx().ceil() |
| 323 | 395 | b_range = range(-bmax, bmax+1) |
| 324 | 396 | |
| 325 | | ## Find the set of all (possibly mutually equivalent) quadratic forms |
| | 397 | # Find the set of all (possibly mutually equivalent) quadratic forms |
| 326 | 398 | form_list = [] |
| 327 | 399 | for b in b_range: |
| 328 | 400 | b_plus = abs(b) |
| 329 | 401 | tmp_num = b**2 - D |
| 330 | 402 | if (b**2 - D) % ZZ(4) == 0: |
| 331 | 403 | tmp_num_4 = tmp_num / ZZ(4) |
| 332 | | b_divs__a = [a for a in divisors(tmp_num_4) if a <= tmp_num_4.sqrt() and b_plus <= a] ## Look for |b| <= a <= c |
| | 404 | # Look for |b| <= a <= c |
| | 405 | b_divs__a = [a for a in divisors(tmp_num_4) if |
| | 406 | a <= tmp_num_4.sqrt() and |
| | 407 | b_plus <= a] |
| 333 | 408 | for a in b_divs__a: |
| 334 | 409 | c = tmp_num_4 / a |
| 335 | | if (a != b_plus and c != b_plus) or (b >= 0): ## Require b>0 if a = c |
| | 410 | if (a != b_plus and c != b_plus) or (b >= 0): |
| | 411 | # Require b>0 if a = c |
| 336 | 412 | form_list.append(BinaryQF([a,b,c])) |
| 337 | | |
| 338 | | ## Return the list |
| 339 | 413 | return form_list |