On 9/12/07, John Voight <[EMAIL PROTECTED]> wrote: > So I took the plunge and started trying to write optimized code in > Cython for my enumeration problem. The code can be found at > http://www.cems.uvm.edu/~voight/tr_data.spyx > http://www.cems.uvm.edu/~voight/totallyreal.py > and for the experts out there, I'd appreciate any feedback you can > give, including tricks and such. I also have a bunch of questions, > some of which are probably just due to my inexperience: > > (1) At a certain moment in the algorithm, I need to test if a > polynomial with integer coefficients is squarefree (using exact > arithmetic). Is it acceptable practice to do this in a brutal way, > like: > > import sage.rings.all PolynomialRing, IntegerRing, gcd > ZZx = PolynomialRing(IntegerRing(), 'x') > [...] > f = ZZx([...]) > df = ZZx([...]) > if gcd(f,df) <> 1: > > What exactly is happening internally there? And what is the overhead > and effect on the timing?
It's building two polynomials over ZZ represented via ntl, then calling the ntl integer GCD function. > (1a) Shouldn't the integer polynomial gcd work using modular > arithmetic to avoid coefficient blow-up? Or does it do this already? > (If gcd(f,g) mod p is 1 for p >> 0 then gcd(f,g) = 1, etc.) I have no idea -- I hope somebody more familiar with NTL internals can just look and see. You could, of course, do the computation modulo a prime right now if you wanted, but you'de have to choose the prime. > (2) I have my Cython code in a tr_data.spyx file and my Python code in > a totallyreal.py file. How do I include the former into the latter-- > or do I have to include both at the prompt? You can't trivially get at tr_data.spyx from totallyreal.py, but you can from a .sage file, as illustrated in this example: [EMAIL PROTECTED]:~/tmp$ more a.py def foo(n): return n*n [EMAIL PROTECTED]:~/tmp$ more b.spyx def bar(m): return m+m [EMAIL PROTECTED]:~/tmp$ more c.sage load a.py load b.spyx print foo(10) print bar(20) [EMAIL PROTECTED]:~/tmp$ sage ---------------------------------------------------------------------- | SAGE Version 2.8.4.1, Release Date: 2007-09-09 | | Type notebook() for the GUI, and license() for information. | ---------------------------------------------------------------------- sage: load c.sage Compiling b.spyx... 100 40 That said, of course you really can get at b.spyx from any .py file as follows: [EMAIL PROTECTED]:~/tmp$ more d.py from sage.all import pyrex pyrex(open("b.spyx").read()) print bar(10) [EMAIL PROTECTED]:~/tmp$ sage ---------------------------------------------------------------------- | SAGE Version 2.8.4.1, Release Date: 2007-09-09 | | Type notebook() for the GUI, and license() for information. | ---------------------------------------------------------------------- sage: import d # pause while b.spyx is automatically compiled. 20 sage: d.bar(30) 60 > (3) I understand that there is significantly more overhead when a By "more" I assume you mean "less". > function is declared using > cdef incr(self) > instead of > def incr(self). > in tr_data.spyx. But if I do this, how do I then access incr in > totallyreal.py? You can't. It doesn't make sense, because cdef incr(self) is just making a C-only level function. Any change you would make to make it possible would immediately put back exactly the overhead you have with def incr(self). > (4) Sometimes I need quite verbose output, when debugging and testing, > and othertimes I just want to turn it all off. Is there a way to set > a global parameter which controls the verbosity for several routines, > like the SetVerbose() in magma? Otherwise, I have to pass a "verbose" > parameter around... There are set_verbose and get_verbose commands in SAGE. This might not work well for you though -- I wrote it right at the beginning, SAGE-0.x days, and haven't changed it since, and I've since learned a lot more about Python. For example, there is a sophisticated logging module: sage: import logging sage: help(logging) That might be better for your application. > (5) Is garbage collection automatic, or do I have to declare a __del__ > function and do a sage_free? Garbage collection of what? Basically, if it's not obvious that garbage collection isn't automatic then it is. For example if you put cdef void* foo = malloc(100000) randomly in the middle of some Cython code, that memory will never be freed unless you explicitly free it. > (6) Craig, did you ever get around to making the line > K1._pari_().nfisisom(K2): > for testing isomorphism between number fields any less ugly? > Shouldn't it read > is_isomorphic(K1, K2)? > While you're at it, I need to be able to run an LLL-optimization > routine to speed up these functions: the function is called polred(). It should be: sage: k.<a> = NumberField(x^2 + 1) sage: m.<b> = NumberField(x^2 + 4) sage: k.is_isomorphic(m) True This is not implemented yet. [... minutes pass ...] This is now implemented. Do hg_sage.pull(); hg_sage.merge(); hg_sage.ci() to get it. > (7) What overhead happens in declaring a number field? Basically nothing. The polynomial is checked for irreducibility and that it is monic. If you do sage: x = polygen(QQ,'x') sage: %prun for i in range(100): k = NumberField(x**3 + x + 1393923489 + i,'a') you'll see that the time is dominated by coercing numbers into the rationals. It's definitey not calling pari's nfinit at all. > Unless I am > reading this incorrectly, it now dominates my computation now! No, pari's nfinit is dominated your computation. That's maybe because you might be using K1._pari_().nfisisom(K2) to check isomorphism. Doing K1._pari_() creates the PARI number field associated to K1, which is a very expensive operation: sage: gp.nfinit? nfinit(pol,{flag=0}): pol being a nonconstant irreducible polynomial, gives the vector: [pol,[r1,r2],discf,index,[M,MC,T2,T,different] (see manual),r1+r2 first roots, integral basis, matrix of power basis in terms of integral basis, multiplication table of basis]. ^^^^^^^^^ With the new K1.is_isomorphic(K2) code I just posted this is completely removed, since it just calls paris nfisisom code with the two polynomials that define the fields as input. > sage: prun enumerate_totallyreal_fields(5,10^5) > 390120 function calls (390117 primitive calls) in 8.774 CPU > seconds > > Ordered by: internal time > > ncalls tottime percall cumtime percall > filename:lineno(function) > 971 3.795 0.004 3.795 0.004 {method 'nfinit' of > 'sage.libs.pari.gen.gen' objects} > 1979 1.024 0.001 1.901 0.001 {method 'is_irreducible' > of 'sage.rings.polynomial.polynomial_element.Polynomial' objects} > 7600 0.403 0.000 0.710 0.000 > polynomial_element_generic.py:946(__init__) > 21686 0.332 0.000 0.381 0.000 rational_field.py: > 120(__call__) > 971 0.230 0.000 0.431 0.000 {method '_pari_' of > 'sage.rings.polynomial.polynomial_element.Polynomial' objects} > 111586 0.223 0.000 0.223 0.000 {isinstance} > 8571 0.211 0.000 1.318 0.000 polynomial_ring.py: > 162(__call__) > 7581 0.197 0.000 0.197 0.000 > {sage.libs.ntl.ntl.make_new_ZZX} > 1996 0.169 0.000 0.419 0.000 > polynomial_element_generic.py:878(list) > 1 0.164 0.164 8.774 8.774 totallyreal.py: > 24(enumerate_totallyreal_fields) > 22151 0.142 0.000 0.226 0.000 > polynomial_element_generic.py:1180(degree) > 971 0.129 0.000 0.247 0.000 > polynomial_element_generic.py:518(__init__) > 45 0.123 0.003 0.123 0.003 {method 'nfisisom' of > 'sage.libs.pari.gen.gen' objects} > 2120 0.119 0.000 0.444 0.000 {method 'incr' of > 'tr_data_spyx_0.tr_data' objects} > 90 0.112 0.001 0.130 0.001 {method '_pari_' of > 'sage.structure.sage_object.SageObject' objects} > 971 0.101 0.000 0.922 0.001 number_field.py: > 157(NumberField) > [...] > > It'd better not be the case that invariants of the field are > automatically computed...! They are -- see above. But that's I'm guessing in your isomorphism code. > (8) When printing a list of integers, why does it print vertically > instead of horizontally, i.e. > [1, > 2, > ... > ] > instead of > [1, 2, ...] > or is this a vagary of Python? It's IPython's pretty printing, I think. Python itself won't do that: [EMAIL PROTECTED]:~/d/sage$ python Python 2.5.1 (r251:54863, May 2 2007, 16:27:44) [GCC 4.1.2 (Ubuntu 4.1.2-0ubuntu4)] on linux2 Type "help", "copyright", "credits" or "license" for more information. >>> range(100) [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, You can also -- I think -- turn it off by modifying ~/.sage/ipython/ipythonrc Finally, if you do sage: print range(100) it all prints on 1 line. If you make a SAGE sequence type it is also all on one line by default, unless you pass cr=True as an option: sage: Sequence(range(100)) [0, 1, 2, 3, 4, 5, 6, 7, 8,... sage: Sequence(range(5), cr=True) [ 0, 1, 2, 3, 4 ] > Thanks, Thanks for your good questions and using SAGE! > > John Voight > Assistant Professor of Mathematics > University of Vermont > [EMAIL PROTECTED] > [EMAIL PROTECTED] > http://www.cems.uvm.edu/~voight/ > > > > > -- William Stein Associate Professor of Mathematics University of Washington http://wstein.org --~--~---------~--~----~------------~-------~--~----~ To post to this group, send email to sage-devel@googlegroups.com To unsubscribe from this group, send email to [EMAIL PROTECTED] For more options, visit this group at http://groups.google.com/group/sage-devel URLs: http://sage.scipy.org/sage/ and http://modular.math.washington.edu/sage/ -~----------~----~----~----~------~----~------~--~---