Ticket #2448: 2448-ncalexan-doc-quadratic_forms-1.patch

File 2448-ncalexan-doc-quadratic_forms-1.patch, 12.8 KB (added by ncalexan, 5 years ago)
  • sage/quadratic_forms/binary_qf.py

    # 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  
    11""" 
    2 Binary Quadratic Forms 
     2Binary Quadratic Forms with integer coefficients. 
     3 
     4The form $a x^2 + b x y + d y^2$ is stored as a triple (a, b, c) of integers. 
     5 
     6EXAMPLES: 
     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 
     17TESTS: 
     18    sage: Q == loads(dumps(Q)) 
     19    True 
    320 
    421AUTHORS: 
    522    -- Jon Hanke (2006-08-08):  
    623        * Appended to add the reduced_representatives, dyadic_trace, 
    724          is_reduced, and + on 8-3-2006 for Coding Sprint #2. 
    825        * 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 
    1227""" 
    1328 
    1429#***************************************************************************** 
    class BinaryQF(SageObject): 
    4560        sage: b.discriminant() 
    4661        -8         
    4762    """ 
    48     ## Initializes the form with a 3-element list 
     63    # Initializes the form with a 3-element list 
    4964    def __init__(self, abc_triple): 
    5065        """ 
    5166        Creates the binary quadratic form $ax^2 + bxy + cy^2$ from the 
    class BinaryQF(SageObject): 
    5469        EXAMPLES: 
    5570            sage: Q = BinaryQF([1,2,3]) 
    5671            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 
    5877        """ 
    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" 
    6081        self.a = ZZ(abc_triple[0]) 
    6182        self.b = ZZ(abc_triple[1]) 
    6283        self.c = ZZ(abc_triple[2]) 
     84        self._reduced_form = None 
    6385 
    6486    def __getitem__(self, n): 
    6587        """ 
     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 
    6695        EXAMPLES: 
    6796            sage: Q = BinaryQF([2,3,4]) 
    6897            sage: Q[0] 
    6998            2 
    7099            sage: Q[2] 
    71100            4 
     101            sage: Q[:2] 
     102            (2, 3) 
    72103            sage: tuple(Q) 
    73104            (2, 3, 4) 
    74105            sage: list(Q) 
    75106            [2, 3, 4] 
    76107        """ 
    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) 
    85123 
    86124    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        """ 
    87147        if not isinstance(right, BinaryQF): 
    88148            return cmp(type(self), type(right)) 
    89149        return cmp((self.a,self.b,self.c), (right.a,right.b,right.c)) 
    90150 
    91151    def __add__(self, Q): 
    92152        """ 
    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 
    94172        """ 
    95173        return BinaryQF([self.a + Q.a, self.b + Q.b, self.c + Q.c]) 
    96174 
     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]) 
    97200 
    98201    def _repr_(self): 
    99202        """ 
    100203        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 
    101214        """ 
    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()) 
    141216 
    142217    def polynomial(self): 
    143218        """ 
    class BinaryQF(SageObject): 
    148223            sage: Q = BinaryQF([1,2,3]) 
    149224            sage: Q.polynomial() 
    150225            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 
    151234        """ 
    152235        M = ZZ['x,y'] 
    153236        (x,y) = M.gens() 
    154237        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  
    170238 
    171239    def discriminant(self): 
    172240        """ 
    class BinaryQF(SageObject): 
    231299            sage: a.is_reduced() 
    232300            False 
    233301            sage: b = a.reduce(); b 
    234             x^2  + xy  + 2y^2 
     302            x^2 + x*y + 2*y^2 
    235303            sage: b.is_reduced() 
    236304            True 
    237305        """ 
    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 
    240310 
    241311    def is_reduced(self): 
    242312        """ 
    class BinaryQF(SageObject): 
    262332            True 
    263333        """ 
    264334        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  
    278335 
    279336    def complex_point(self): 
    280337        """ 
    281338        Returns the point in the complex upper half-plane associated 
    282339        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. 
    283344 
    284345        EXAMPLES: 
    285346            sage: Q = BinaryQF([1,0,1]) 
    class BinaryQF(SageObject): 
    296357 
    297358def BinaryQF_reduced_representatives(D): 
    298359    """ 
    299     Returns inequivalent reduced representatives for the equivalence 
     360    Returns a list of inequivalent reduced representatives for the equivalence 
    300361    classes of positive definite binary forms of discriminant D. 
    301362 
    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! 
    304367 
    305368    EXAMPLES: 
    306369        sage: BinaryQF_reduced_representatives(-4) 
    307         [x^2  + y^2 ] 
     370        [x^2 + y^2] 
    308371         
    309372        sage: BinaryQF_reduced_representatives(-163) 
    310         [x^2  + xy  + 41y^2 ] 
     373        [x^2 + x*y + 41*y^2] 
    311374         
    312375        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] 
    314377 
    315378        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 
    317389    """ 
    318390    if not ( D < 0  and  D in ZZ  and ((D % ZZ(4) == 0) or (D % ZZ(4) == 1))): 
    319391        raise ValueError, "discriminant is not valid and positive definite" 
    320392 
    321     ## Find the range of allowed b's 
     393    # Find the range of allowed b's 
    322394    bmax = (-D / ZZ(3)).sqrt_approx().ceil() 
    323395    b_range = range(-bmax, bmax+1) 
    324396     
    325     ## Find the set of all (possibly mutually equivalent) quadratic forms 
     397    # Find the set of all (possibly mutually equivalent) quadratic forms 
    326398    form_list = [] 
    327399    for b in b_range: 
    328400        b_plus = abs(b) 
    329401        tmp_num = b**2 - D 
    330402        if (b**2 - D) % ZZ(4) == 0: 
    331403            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] 
    333408            for a in b_divs__a: 
    334409                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 
    336412                    form_list.append(BinaryQF([a,b,c])) 
    337  
    338     ## Return the list 
    339413    return form_list