<?xml version='1.0' encoding='UTF-8'?><?xml-stylesheet href="http://www.blogger.com/styles/atom.css" type="text/css"?><feed xmlns='http://www.w3.org/2005/Atom' xmlns:openSearch='http://a9.com/-/spec/opensearchrss/1.0/' xmlns:georss='http://www.georss.org/georss' xmlns:gd='http://schemas.google.com/g/2005' xmlns:thr='http://purl.org/syndication/thread/1.0'><id>tag:blogger.com,1999:blog-4754734402679928849</id><updated>2012-01-26T13:59:57.742+01:00</updated><category term='matplotlib'/><category term='flint'/><category term='sympy'/><category term='sage'/><category term='mpmath'/><category term='music'/><category term='math'/><category term='gsoc'/><category term='cython'/><category term='python'/><category term='sage days'/><title type='text'>Fredrik Johansson</title><subtitle type='html'></subtitle><link rel='http://schemas.google.com/g/2005#feed' type='application/atom+xml' href='http://fredrik-j.blogspot.com/feeds/posts/default'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4754734402679928849/posts/default?max-results=100'/><link rel='alternate' type='text/html' href='http://fredrik-j.blogspot.com/'/><link rel='hub' href='http://pubsubhubbub.appspot.com/'/><author><name>Fredrik Johansson</name><uri>http://www.blogger.com/profile/01465400860530971858</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><generator version='7.00' uri='http://www.blogger.com'>Blogger</generator><openSearch:totalResults>64</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>100</openSearch:itemsPerPage><entry><id>tag:blogger.com,1999:blog-4754734402679928849.post-980280886537837847</id><published>2012-01-26T13:58:00.003+01:00</published><updated>2012-01-26T13:59:57.750+01:00</updated><title type='text'>Move along</title><content type='html'>I'm moving my blog to &lt;a href="http://fredrikj.net/blog"&gt;http://fredrikj.net/blog&lt;/a&gt;. If you're living in the future and reading this, you should go there instead!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4754734402679928849-980280886537837847?l=fredrik-j.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://fredrik-j.blogspot.com/feeds/980280886537837847/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=4754734402679928849&amp;postID=980280886537837847' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4754734402679928849/posts/default/980280886537837847'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4754734402679928849/posts/default/980280886537837847'/><link rel='alternate' type='text/html' href='http://fredrik-j.blogspot.com/2012/01/move-along.html' title='Move along'/><author><name>Fredrik Johansson</name><uri>http://www.blogger.com/profile/01465400860530971858</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4754734402679928849.post-7181919645644287701</id><published>2011-06-08T10:58:00.004+02:00</published><updated>2011-06-08T18:41:59.740+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='sage'/><category scheme='http://www.blogger.com/atom/ns#' term='sympy'/><category scheme='http://www.blogger.com/atom/ns#' term='mpmath'/><category scheme='http://www.blogger.com/atom/ns#' term='flint'/><title type='text'>Some FLINT 2.2 highlights</title><content type='html'>Version 2.2 of &lt;a href="http://flintlib.org/"&gt;FLINT&lt;/a&gt; (Fast Library for Number Theory) was &lt;a href="https://groups.google.com/group/flint-devel/browse_thread/thread/442a98d8bc31470a?hl=en"&gt;released&lt;/a&gt; last weekend. Some updated &lt;a href="http://sage.math.washington.edu/home/fredrik/flint/timings.html"&gt;benchmarks&lt;/a&gt; are available.&lt;br /&gt;&lt;br /&gt;In this blog post, I'm going to talk a bit about features I contributed in this version. With apologies to &lt;a href="https://github.com/SPancratz/flint2/"&gt;Sebastian Pancratz&lt;/a&gt; who wrote a whole lot of code as well -- in particular, a new module for computing with p-adic numbers, and a module for rational functions! Bill Hart also implemented a faster polynomial GCD, which is a quite important update since GCD is crucial for most polynomial business. Anyhow...&lt;br /&gt;&lt;br /&gt;&lt;h2&gt;Polynomial interpolation&lt;/h2&gt;&lt;br /&gt;I've added various functions for polynomial interpolation to the &lt;a href="https://github.com/fredrik-johansson/flint2/tree/trunk/fmpz_poly_mat"&gt;fmpz_poly&lt;/a&gt; module. In general, these can be used to speed up various computations involving integer or rational polynomials by mapping a given problem to (Z/nZ)[x], Z or even Z/nZ, taking advantage of fast arithmetic in those rings, and then via interpolation recovering a result in Z[x] or Q[x].&lt;br /&gt;&lt;br /&gt;Firstly, there are some new Chinese Remainder Theorem functions for integer polynomials, allowing you to reconstruct an integer polynomial from a bunch of polynomials with coefficients modulo different primes. Straightforward code (the actual work is done by functions in the fmpz module), but useful to have. The CRT functions are used by the new modular GCD code.&lt;br /&gt;&lt;br /&gt;There are also functions for evaluating an integer polynomial on a set of points, and forming the interpolating polynomial (generally with rational coefficients) given a set of points and the values at those points.&lt;br /&gt;&lt;br /&gt;Finally, user-friendly functions for evaluation and interpolation at a power of two (Kronecker segmentation) have been added. The code for this is actually a very old part of FLINT, and possibly some of the most complicated code in the library (packing bits efficiently is surprisingly hard). The new functions just wrap this functionality, but take care of memory management and various special cases, so you can now just safely do something like:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;    fmpz_t z;&lt;br /&gt;    fmpz_init(z);&lt;br /&gt;    long bits = fmpz_poly_max_bits(poly) + 1; /* +1 for signs */&lt;br /&gt;    fmpz_poly_bit_pack(z, poly, bits);&lt;br /&gt;    fmpz_poly_bit_unpack(poly, z, bits);  /* recover poly */&lt;br /&gt;    fmpz_clear(z);&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Apart from Kronecker segmentation, these functions are not currently asymptotically fast. Fast multi-modulus CRT for coefficient reconstruction is probably not all that important in most circumstances, because it's more common to use evaluation-interpolation techniques for polynomials of large degree and small coefficients than the other way around. Nonetheless, polynomials with large coefficients do arise as well. For example, the vector Bernoulli number code in FLINT relies on fast CRT, and currently uses custom code for this.&lt;br /&gt;&lt;br /&gt;Polynomial interpolation uses &lt;a href="http://en.wikipedia.org/wiki/Lagrange_polynomial"&gt;Lagrange interpolation&lt;/a&gt; with barycentric weights, with a few tricks to avoid fractions. This is all implemented using an O(n^2) algorithm, but the actual time complexity is higher due to the fact that the coefficients when working over integers usually will be large, around n! in magnitude.&lt;br /&gt;&lt;br /&gt;Here are some timing examples, evaluating and recovering a length-n polynomial with +/-1 coefficients basically as follows:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;    x = _fmpz_vec_init(n);&lt;br /&gt;    y = _fmpz_vec_init(n);&lt;br /&gt;    fmpz_poly_init(P);&lt;br /&gt;    fmpz_poly_init(Q);&lt;br /&gt;&lt;br /&gt;    for (i = 0; i &lt; n; i++)&lt;br /&gt;        x[i] = -n/2 + i;&lt;br /&gt;&lt;br /&gt;    fmpz_poly_randtest(P, state, n, 1);&lt;br /&gt;&lt;br /&gt;    fmpz_poly_evaluate_fmpz_vec(y, P, x, n);&lt;br /&gt;    fmpz_poly_interpolate_fmpz_vec(Q, x, y, n);&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;The bits column below measures the largest value in y, which grows quite large despite the input polynomial having small coefficients:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;    n=8  eval=762 ns  interp=13 us  bits=8  ok=1&lt;br /&gt;    n=16  eval=3662 ns  interp=61 us  bits=42  ok=1&lt;br /&gt;    n=32  eval=29 us  interp=673 us  bits=-113  ok=1&lt;br /&gt;    n=64  eval=136 us  interp=4951 us  bits=-316  ok=1&lt;br /&gt;    n=128  eval=625 us  interp=45 ms  bits=-762  ok=1&lt;br /&gt;    n=256  eval=2500 us  interp=792 ms  bits=-1779  ok=1&lt;br /&gt;    n=512  eval=12 ms  interp=10 s  bits=-4089  ok=1&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;As you can see, the interpolation speed is not too bad for small n, but eventually grows out of control. How to do better?&lt;br /&gt;&lt;br /&gt;Naive Lagrange interpolation is not optimal: it is possible to do n-point evaluation and interpolation in essentially O(n log&lt;sup&gt;2&lt;/sup&gt; n) operations. Such algorithms do not necessarily lead to an improvement over the integers (you still have to deal with coefficient explosion), but they should win over finite fields. So the right solution will perhaps be to add polynomial evaluation/interpolation functions based on modular arithmetic.&lt;br /&gt;&lt;br /&gt;&lt;h2&gt;Rational numbers and matrices&lt;/h2&gt;&lt;br /&gt;&lt;br /&gt;A new module &lt;a href="https://github.com/fredrik-johansson/flint2/tree/trunk/fmpq"&gt;fmpq&lt;/a&gt; is provided for computing with arbitrary-precision rational numbers. For the user, the fmpq_t type essentially behaves identically to the MPIR mpq_t type. However, an fmpq_t only takes up two words of memory when the numerator and denominator are small (less than 2&lt;sup&gt;62&lt;/sup&gt;), whereas an mpq_t always requires six words plus additional heap-allocated space for the actual number data.&lt;br /&gt;&lt;br /&gt;The fmpq functions are a bit faster than mpq functions in many cases when the numerator and/or denominator is small. But the main improvement should come for vectors, matrices or polynomials of rational numbers, due to the significantly reduced memory usage and memory management overhead (especially when many entries are zero or integer-valued).&lt;br /&gt;&lt;br /&gt;Some higher-level functionality is also provided in the fmpq module, e.g. for rational reconstruction. The functions for computing special rational numbers (like Bernoulli numbers) have also been switched over to the fmpq type. Another supported feature is enumeration of the rationals (using the &lt;a href="http://en.wikipedia.org/wiki/Calkin%E2%80%93Wilf_tree"&gt;Calkin-Wilf&lt;/a&gt; sequence or by height). Generating the 100 million "first" positive rational numbers takes 9.6 seconds done in order of height, or 2.6 seconds in Calkin-Wilf order.&lt;br /&gt;&lt;br /&gt;FLINT actually does not use fmpq's to represent polynomials over Q (fmpq_poly), and probably never will. The fmpq_poly module represents a polynomial over Q as an integer polynomial with a single common denominator, which is usually faster. The reason for adding the fmpq_t type is that it enabled developing the new &lt;a href="https://github.com/fredrik-johansson/flint2/tree/trunk/fmpq_mat"&gt;fmpq_mat&lt;/a&gt; module, which implements dense matrices of rational numbers. For matrices, a common-denominator representation would be less convenient and in many cases completely impractical.&lt;br /&gt;&lt;br /&gt;The new FLINT fmpq_mat module is very fast, or at least very non-slow. It is easy to find examples where it does a simple computation a thousand times faster than the rational matrices in Sage.&lt;br /&gt;&lt;br /&gt;There's not actually much code in the fmpq_mat module itself; it does almost all "level 3" linear algebra (computations requiring matrix multiplication or Gaussian elimination) by clearing denominators and computing over the integers. This approach is in fact stolen shamelessly from Sage, but the functions in Sage are highly unoptimized in many cases. The code in Sage still wins for many sufficiently large problems as it has asymptotically fast algorithms for many things we do not (like computing null spaces). See the &lt;a href="http://sage.math.washington.edu/home/fredrik/flint/timings.html"&gt;benchmarks page&lt;/a&gt; for more details.&lt;br /&gt;&lt;br /&gt;I should not forget to mention that I've implemented Dixon's p-adic algorithm for solving Ax = b for nonsingular square A. (I wish I had a good link for Dixon's algorithm here, but sadly it doesn't appear to be described conveniently anywhere on the web. The original paper is "&lt;a href="http://www.springerlink.com/content/g711u0m541351u71/"&gt;Exact solution of linear equations using P-adic expansions&lt;/a&gt;", if you have the means to get through the Springer paywall.)&lt;br /&gt;&lt;br /&gt;This is now used both for solving over both Z and Q. The solver in FLINT is competitive with Sage (which uses &lt;a href="http://www.cs.uwaterloo.ca/~astorjoh/iml.html"&gt;IML&lt;/a&gt;+&lt;a href="http://math-atlas.sourceforge.net/"&gt;ATLAS&lt;/a&gt;) up to systems of dimension somewhere between perhaps 100 and 1000 (depending greatly on the size of the entries in the inputs and in the solution!). There's much to do here -- we should eventually have BLAS support in FLINT, which will speed up core matrix arithmetic, but there's room for a lot of algorithmic tuning as well.&lt;br /&gt;&lt;br /&gt;There are some other minor new matrix features as well... they can be found in the changelog.&lt;br /&gt;&lt;br /&gt;&lt;h2&gt;Polynomial matrices&lt;/h2&gt;&lt;br /&gt;&lt;br /&gt;A new module (&lt;a href="https://github.com/fredrik-johansson/flint2/tree/trunk/fmpz_poly_mat"&gt;fmpz_poly_mat&lt;/a&gt;) is provided for dense matrices over Z[x], i.e. matrices whose entries are polynomials with integer coefficients. The available functionality includes matrix multiplication, row reduction, and determinants. Matrix multiplication is particularly fast, as it uses the Kronecker segmentation interpolation/evaluation technique described above. (A similar algorithm is provided for determinants, but it's not really optimal as this point.)&lt;br /&gt;&lt;br /&gt;The benchmarks page has detailed some detailed timings, so I won't repeat them here -- but generally speaking, the FLINT implementation is an order of magnitude faster than Sage or Magma for matrices of manageable size.&lt;br /&gt;&lt;br /&gt;There's much more to be done for polynomial matrices. Row reduction is implemented quite efficiently, but it's too slow as an algorithm for many tasks such as computing null spaces of very large matrices. A future goal is to implement asymptotically fast algorithms (see the &lt;a href="http://portal.acm.org/citation.cfm?id=1940489"&gt;paper on x-adic lifting&lt;/a&gt; by Burçin Eröcal and Arne Storjohann for example).&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4754734402679928849-7181919645644287701?l=fredrik-j.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://fredrik-j.blogspot.com/feeds/7181919645644287701/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=4754734402679928849&amp;postID=7181919645644287701' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4754734402679928849/posts/default/7181919645644287701'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4754734402679928849/posts/default/7181919645644287701'/><link rel='alternate' type='text/html' href='http://fredrik-j.blogspot.com/2011/06/some-flint-22-highlights.html' title='Some FLINT 2.2 highlights'/><author><name>Fredrik Johansson</name><uri>http://www.blogger.com/profile/01465400860530971858</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4754734402679928849.post-7530661868168314621</id><published>2011-03-14T16:37:00.006+01:00</published><updated>2011-04-16T19:39:13.279+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='sage'/><category scheme='http://www.blogger.com/atom/ns#' term='sympy'/><category scheme='http://www.blogger.com/atom/ns#' term='mpmath'/><title type='text'>100 mpmath one-liners for pi</title><content type='html'>Since it's pi day today, I thought I'd share a list of mpmath one-liners for computing the value of pi to high precision using various representations in terms of special functions, infinite series, integrals, etc. Most of them can already be found as doctest examples in some form in the mpmath documentation.&lt;br /&gt;&lt;br /&gt;A few of the formulas explicitly involve pi. Using those to calculate pi is rather &lt;i&gt;circular&lt;/i&gt; (!), though a few of them could still be used for computing pi using numerical root-finding. In any case, most of the formulas are circular even when pi doesn't appear explicitly since mpmath is likely using its value internally. In any &lt;i&gt;further&lt;/i&gt; case, the majority of the formulas are not efficient for computing pi to very high precision (at least as written). Still, ~50 digits is no problem. Enjoy!&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;from mpmath import *&lt;br /&gt;mp.dps = 50; mp.pretty = True&lt;br /&gt;&lt;br /&gt;+pi&lt;br /&gt;180*degree&lt;br /&gt;4*atan(1)&lt;br /&gt;16*acot(5)-4*acot(239)&lt;br /&gt;48*acot(49)+128*acot(57)-20*acot(239)+48*acot(110443)&lt;br /&gt;chop(2*j*log((1-j)/(1+j)))&lt;br /&gt;chop(-2j*asinh(1j))&lt;br /&gt;chop(ci(-inf)/1j)&lt;br /&gt;gamma(0.5)**2&lt;br /&gt;beta(0.5,0.5)&lt;br /&gt;(2/diff(erf, 0))**2&lt;br /&gt;findroot(sin, 3)&lt;br /&gt;findroot(cos, 1)*2&lt;br /&gt;chop(-2j*lambertw(-pi/2))&lt;br /&gt;besseljzero(0.5,1)&lt;br /&gt;3*sqrt(3)/2/hyp2f1((-1,3),(1,3),1,1)&lt;br /&gt;8/(hyp2f1(0.5,0.5,1,0.5)*gamma(0.75)/gamma(1.25))**2&lt;br /&gt;4*(hyp1f2(1,1.5,1,1) / struvel(-0.5, 2))**2&lt;br /&gt;1/meijerg([[],[]], [[0],[0.5]], 0)**2&lt;br /&gt;(meijerg([[],[2]], [[1,1.5],[]], 1, 0.5) / erfc(1))**2&lt;br /&gt;(1-e) / meijerg([[1],[0.5]], [[1],[0.5,0]], 1)&lt;br /&gt;sqrt(psi(1,0.25)-8*catalan)&lt;br /&gt;elliprc(1,2)*4&lt;br /&gt;elliprg(0,1,1)*4&lt;br /&gt;2*agm(1,0.5)*ellipk(0.75)&lt;br /&gt;(gamma(0.75)*jtheta(3,0,exp(-pi)))**4&lt;br /&gt;cbrt(gamma(0.25)**4*agm(1,sqrt(2))**2/8)&lt;br /&gt;sqrt(6*zeta(2))&lt;br /&gt;sqrt(6*(zeta(2,3)+5./4))&lt;br /&gt;sqrt(zeta(2,(3,4))+8*catalan)&lt;br /&gt;exp(-2*zeta(0,1,1))/2&lt;br /&gt;sqrt(12*altzeta(2))&lt;br /&gt;4*dirichlet(1,[0,1,0,-1])&lt;br /&gt;2*catalan/dirichlet(-1,[0,1,0,-1],1)&lt;br /&gt;exp(-dirichlet(0,[0,1,0,-1],1))*gamma(0.25)**2/(2*sqrt(2))&lt;br /&gt;sqrt(7*zeta(3)/(4*diff(lerchphi, (-1,-2,1), (0,1,0))))&lt;br /&gt;sqrt(-12*polylog(2,-1))&lt;br /&gt;sqrt(6*log(2)**2+12*polylog(2,0.5))&lt;br /&gt;chop(root(-81j*(polylog(3,root(1,3,1))+4*zeta(3)/9)/2,3))&lt;br /&gt;2*clsin(1,1)+1&lt;br /&gt;(3+sqrt(3)*sqrt(1+8*clcos(2,1)))/2&lt;br /&gt;root(2,6)*sqrt(e)/(glaisher**6*barnesg(0.5)**4)&lt;br /&gt;nsum(lambda k: 4*(-1)**(k+1)/(2*k-1), [1,inf])&lt;br /&gt;nsum(lambda k: (3**k-1)/4**k*zeta(k+1), [1,inf])&lt;br /&gt;nsum(lambda k: 8/(2*k-1)**2, [1,inf])**0.5&lt;br /&gt;nsum(lambda k: 2*fac(k)/fac2(2*k+1), [0,inf])&lt;br /&gt;nsum(lambda k: fac(k)**2/fac(2*k+1), [0,inf])*3*sqrt(3)/2&lt;br /&gt;nsum(lambda k: fac(k)**2/(phi**(2*k+1)*fac(2*k+1)), [0,inf])*(5*sqrt(phi+2))/2&lt;br /&gt;nsum(lambda k: (4/(8*k+1)-2/(8*k+4)-1/(8*k+5)-1/(8*k+6))/16**k, [0,inf])&lt;br /&gt;2/nsum(lambda k: (-1)**k*(4*k+1)*(fac2(2*k-1)/fac2(2*k))**3, [0,inf])&lt;br /&gt;nsum(lambda k: 72/(k*expm1(k*pi))-96/(k*expm1(2*pi*k))+24/(k*expm1(4*pi*k)), [1,inf])&lt;br /&gt;1/nsum(lambda k: binomial(2*k,k)**3*(42*k+5)/2**(12*k+4), [0,inf])&lt;br /&gt;4/nsum(lambda k: (-1)**k*(1123+21460*k)*fac2(2*k-1)*fac2(4*k-1)/(882**(2*k+1)*32**k*fac(k)**3), [0,inf])&lt;br /&gt;9801/sqrt(8)/nsum(lambda k: fac(4*k)*(1103+26390*k)/(fac(k)**4*396**(4*k)), [0,inf])&lt;br /&gt;426880*sqrt(10005)/nsum(lambda k: (-1)**k*fac(6*k)*(13591409+545140134*k)/(fac(k)**3*fac(3*k)*(640320**3)**k), [0,inf])&lt;br /&gt;4/nsum(lambda k: (6*k+1)*rf(0.5,k)**3/(4**k*fac(k)**3), [0,inf])&lt;br /&gt;(ln(8)+sqrt(48*nsum(lambda m,n: (-1)**(m+n)/(m**2+n**2), [1,inf],[1,inf]) + 9*log(2)**2))/2&lt;br /&gt;-nsum(lambda x,y: (-1)**(x+y)/(x**2+y**2), [-inf,inf], [-inf,inf], ignore=True)/ln2&lt;br /&gt;2*nsum(lambda k: sin(k)/k, [1,inf])+1&lt;br /&gt;quad(lambda x: 2/(x**2+1), [0,inf])&lt;br /&gt;quad(lambda x: exp(-x**2), [-inf,inf])**2&lt;br /&gt;2*quad(lambda x: sqrt(1-x**2), [-1,1])&lt;br /&gt;chop(quad(lambda z: 1/(2j*z), [1,j,-1,-j,1]))&lt;br /&gt;3*(4*log(2+sqrt(3))-quad(lambda x,y: 1/sqrt(1+x**2+y**2), [-1,1],[-1,1]))/2&lt;br /&gt;sqrt(8*quad(lambda x,y: 1/(1-(x*y)**2), [0,1],[0,1]))&lt;br /&gt;sqrt(6*quad(lambda x,y: 1/(1-x*y), [0,1],[0,1]))&lt;br /&gt;sqrt(6*quad(lambda x: x/expm1(x), [0,inf]))&lt;br /&gt;quad(lambda x: (16*x-16)/(x**4-2*x**3+4*x-4), [0,1])&lt;br /&gt;quad(lambda x: sqrt(x-x**2), [0,0.25])*24+3*sqrt(3)/4&lt;br /&gt;mpf(22)/7 - quad(lambda x: x**4*(1-x)**4/(1+x**2), [0,1])&lt;br /&gt;mpf(355)/113 - quad(lambda x: x**8*(1-x)**8*(25+816*x**2)/(1+x**2), [0,1])/3164&lt;br /&gt;2*quadosc(lambda x: sin(x)/x, [0,inf], omega=1)&lt;br /&gt;40*quadosc(lambda x: sin(x)**6/x**6, [0,inf], omega=1)/11&lt;br /&gt;e*quadosc(lambda x: cos(x)/(1+x**2), [-inf,inf], omega=1)&lt;br /&gt;8*quadosc(lambda x: cos(x**2), [0,inf], zeros=lambda n: sqrt(n))**2&lt;br /&gt;2*quadosc(lambda x: sin(exp(x)), [1,inf], zeros=ln)+2*si(e)&lt;br /&gt;exp(2*quad(loggamma, [0,1]))/2&lt;br /&gt;2*nprod(lambda k: sec(pi/2**k), [2,inf])&lt;br /&gt;s=lambda k: sqrt(0.5+s(k-1)/2) if k else 0; 2/nprod(s, [1,inf])&lt;br /&gt;s=lambda k: sqrt(2+s(k-1)) if k else 0; limit(lambda k: sqrt(2-s(k))*2**(k+1), inf)&lt;br /&gt;2*nprod(lambda k: (2*k)**2/((2*k-1)*(2*k+1)), [1,inf])&lt;br /&gt;2*nprod(lambda k: (4*k**2)/(4*k**2-1), [1, inf])&lt;br /&gt;sqrt(6*ln(nprod(lambda k: exp(1/k**2), [1,inf])))&lt;br /&gt;nprod(lambda k: (k**2-1)/(k**2+1), [2,inf])/csch(pi)&lt;br /&gt;nprod(lambda k: (k**2-1)/(k**2+1), [2,inf])*sinh(pi)&lt;br /&gt;nprod(lambda k: (k**4-1)/(k**4+1), [2, inf])*(cosh(sqrt(2)*pi)-cos(sqrt(2)*pi))/sinh(pi)&lt;br /&gt;sinh(pi)/nprod(lambda k: (1-1/k**4), [2, inf])/4&lt;br /&gt;sinh(pi)/nprod(lambda k: (1+1/k**2), [2, inf])/2&lt;br /&gt;(exp(1+euler/2)/nprod(lambda n: (1+1/n)**n * exp(1/(2*n)-1), [1, inf]))**2/2&lt;br /&gt;3*sqrt(2)*cosh(pi*sqrt(3)/2)**2*csch(pi*sqrt(2))/nprod(lambda k: (1+1/k+1/k**2)**2/(1+2/k+3/k**2), [1, inf])&lt;br /&gt;2/e*nprod(lambda k: (1+2/k)**((-1)**(k+1)*k), [1,inf])&lt;br /&gt;limit(lambda k: 16**k/(k*binomial(2*k,k)**2), inf)&lt;br /&gt;limit(lambda x: 4*x*hyp1f2(0.5,1.5,1.5,-x**2), inf)&lt;br /&gt;1/log(limit(lambda n: nprod(lambda k: pi/(2*atan(k)), [n,2*n]), inf),4)&lt;br /&gt;limit(lambda k: 2**(4*k+1)*fac(k)**4/(2*k+1)/fac(2*k)**2, inf)&lt;br /&gt;limit(lambda k: fac(k) / (sqrt(k)*(k/e)**k), inf)**2/2&lt;br /&gt;limit(lambda k: (-(-1)**k*bernoulli(2*k)*2**(2*k-1)/fac(2*k))**(-1/(2*k)), inf)&lt;br /&gt;limit(lambda k: besseljzero(1,k)/k, inf)&lt;br /&gt;1/limit(lambda x: airyai(x)*2*x**0.25*exp(2*x**1.5/3), inf, exp=True)**2&lt;br /&gt;1/limit(lambda x: airybi(x)*x**0.25*exp(-2*x**1.5/3), inf, exp=True)**2&lt;br /&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4754734402679928849-7530661868168314621?l=fredrik-j.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://fredrik-j.blogspot.com/feeds/7530661868168314621/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=4754734402679928849&amp;postID=7530661868168314621' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4754734402679928849/posts/default/7530661868168314621'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4754734402679928849/posts/default/7530661868168314621'/><link rel='alternate' type='text/html' href='http://fredrik-j.blogspot.com/2011/03/100-mpmath-one-liners-for-pi.html' title='100 mpmath one-liners for pi'/><author><name>Fredrik Johansson</name><uri>http://www.blogger.com/profile/01465400860530971858</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4754734402679928849.post-3253966970864417650</id><published>2011-03-11T13:28:00.010+01:00</published><updated>2011-03-11T16:37:53.331+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='sage'/><category scheme='http://www.blogger.com/atom/ns#' term='sympy'/><category scheme='http://www.blogger.com/atom/ns#' term='mpmath'/><title type='text'>A FLINT example: Lambert W function power series</title><content type='html'>Two days ago, a new version of the &lt;a href="http://flintlib.org/"&gt;Fast Library for Number Theory (FLINT)&lt;/a&gt; was released. I contributed a lot of new code to this release, including linear algebra speed improvements and new functionality for fast power series arithmetic and computation of special numbers and polynomials (see the &lt;a href="https://groups.google.com/group/flint-devel/browse_thread/thread/759709de4720633f?hl=en"&gt;release announcement&lt;/a&gt; and some of my &lt;a href="http://sage.math.washington.edu/home/fredrik/flint/timings.html"&gt;benchmarking results&lt;/a&gt;).&lt;br /&gt;&lt;br /&gt;In this blog post I'll demonstrate how to do power series arithmetic with FLINT, using its &lt;tt&gt;fmpq_poly&lt;/tt&gt; module which implements polynomials over the rational numbers Q. Standard operations  (addition, multiplication and division) were available before; the functions I've added include square root, log, exp, sin, tan, atan, etc. (all the usual elementary functions). The same functions are also available for power series over a finite field Z/nZ (with word-size n). Everything is asymptotically fast (the running time is linear in the size of the output, up to logarithmic factors).&lt;br /&gt;&lt;br /&gt;Of course, transcendental functions are a bit restricted when considered over Q or Z/nZ, since it's only possible to obtain power series expansions at specific rational points (in most cases just x = 0). So at present, some very interesting numerical applications of fast power series arithmetic are not supported. But some time in the future, we'll probably add support for numerical power series over the reals and complexes as well.&lt;br /&gt;&lt;br /&gt;As today's example, let us implement the &lt;a href="http://en.wikipedia.org/wiki/Lambert_W_function"&gt;Lambert W function&lt;/a&gt; for the power series ring Q[[x]]. The Lambert W function is defined implicitly by the equation x = W(x) exp(W(z)), which can be solved using Newton iteration with the update step w = w - (w exp(w) - x) / ((w+1) exp(w)).&lt;br /&gt;&lt;br /&gt;Power series Newton iteration is just like numerical Newton iteration, except that the convergence behavior is much simpler: starting with a correct first-order expansion, each iteration at least doubles the number of correct coefficients.&lt;br /&gt;&lt;br /&gt;A simple recursive implementation with asymptotically optimal performance (up to constant factors) looks as follows:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;#include &amp;lt;stdio.h&gt;&lt;br /&gt;#include "flint.h"&lt;br /&gt;#include "fmpq_poly.h"&lt;br /&gt;&lt;br /&gt;void lambertw(fmpq_poly_t w, fmpq_poly_t x, long n)&lt;br /&gt;{&lt;br /&gt;    if (n == 1)&lt;br /&gt;    {&lt;br /&gt;        fmpq_poly_zero(w);&lt;br /&gt;    }&lt;br /&gt;    else&lt;br /&gt;    {&lt;br /&gt;        fmpq_poly_t t, u, v;&lt;br /&gt;&lt;br /&gt;        lambertw(w, x, (n + 1) / 2);&lt;br /&gt;&lt;br /&gt;        fmpq_poly_init(t);&lt;br /&gt;        fmpq_poly_init(u);&lt;br /&gt;        fmpq_poly_init(v);&lt;br /&gt;&lt;br /&gt;        fmpq_poly_exp_series(t, w, n);&lt;br /&gt;        fmpq_poly_mullow(u, t, w, n);&lt;br /&gt;        fmpq_poly_sub(v, u, x);&lt;br /&gt;        fmpq_poly_add(t, u, t);&lt;br /&gt;        fmpq_poly_div_series(u, v, t, n);&lt;br /&gt;        fmpq_poly_sub(w, w, u);&lt;br /&gt;&lt;br /&gt;        fmpq_poly_clear(t);&lt;br /&gt;        fmpq_poly_clear(u);&lt;br /&gt;        fmpq_poly_clear(v);&lt;br /&gt;    }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Beyond the base case W(x) = 0 + O(x), the function just computes w to accuracy ceil(n/2), and then extends it to accuracy n using a single Newton step. As we can see, C code directly using the FLINT library interface gets a bit verbose, but this style has the advantage of giving precise control over temporary memory allocation, polynomial lengths, etc. (it is very similar to the interface of GMP/MPIR).&lt;br /&gt;&lt;br /&gt;We add a simple test main routine:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;int main()&lt;br /&gt;{&lt;br /&gt;   fmpq_poly_t x;&lt;br /&gt;   fmpq_poly_t w;&lt;br /&gt;&lt;br /&gt;   fmpq_poly_init(x);&lt;br /&gt;   fmpq_poly_init(w);&lt;br /&gt;&lt;br /&gt;   fmpq_poly_set_coeff_ui(x, 1, 1);&lt;br /&gt;   lambertw(w, x, 10);&lt;br /&gt;&lt;br /&gt;   fmpq_poly_print_pretty(w, "x");&lt;br /&gt;   printf("\n");&lt;br /&gt;&lt;br /&gt;   fmpq_poly_clear(x);&lt;br /&gt;   fmpq_poly_clear(w);&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;The output of the program is:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;531441/4480*x^9 - 16384/315*x^8 + 16807/720*x^7 - 54/5*x^6 + 125/24*x^5 - 8/3*x^4 + 3/2*x^3 - 1*x^2 + 1*x&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;It is well known that the coefficients in this series are given in closed form by (-k)&lt;sup&gt;k-1&lt;/sup&gt; / k!, so we can check that the output is correct.&lt;br /&gt;&lt;br /&gt;Computing 1000 terms takes just a few seconds. If this sounds like much, remember that the coefficients grow rapidly: together, the computed numerators and denominators have over 2 million digits!&lt;br /&gt;&lt;br /&gt;So far this is perhaps not so interesting, as we could compute the coefficients faster using a direct formula. But the nice thing is that arbitrary compositions are allowed, i.e we can compute W(f(x)) for any given power series f, and this will still be just as fast.&lt;br /&gt;&lt;br /&gt;Let's consider a nontrivial example: the infinite "power tower" T(z) = z&lt;sup&gt;z&lt;sup&gt;z&lt;sup&gt;z&lt;sup&gt;.&lt;sup&gt;.&lt;/sup&gt;&lt;/sup&gt;&lt;/sup&gt;&lt;/sup&gt;&lt;/sup&gt;. A moment's reflection shows that this is an analytic function with a rational power series expansion around z = 1. In fact, we have explicitly T(z) = W(-log(z))/(-log(z)). We can compute this series expansion (in the shifted variable x = z - 1) as follows:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;int main()&lt;br /&gt;{&lt;br /&gt;    fmpq_poly_t x;&lt;br /&gt;    fmpq_poly_t w;&lt;br /&gt;&lt;br /&gt;    long n = 10;&lt;br /&gt;&lt;br /&gt;    fmpq_poly_init(x);&lt;br /&gt;    fmpq_poly_init(w);&lt;br /&gt;&lt;br /&gt;    fmpq_poly_set_coeff_ui(x, 0, 1);&lt;br /&gt;    fmpq_poly_set_coeff_ui(x, 1, 1);&lt;br /&gt;    fmpq_poly_log_series(x, x, n + 1);&lt;br /&gt;    fmpq_poly_neg(x, x);&lt;br /&gt;    lambertw(w, x, n + 1);&lt;br /&gt;    fmpq_poly_shift_right(w, w, 1);&lt;br /&gt;    fmpq_poly_shift_right(x, x, 1);&lt;br /&gt;    fmpq_poly_div_series(w, w, x, n);&lt;br /&gt;&lt;br /&gt;    fmpq_poly_print_pretty(w, "x");&lt;br /&gt;    printf("\n");&lt;br /&gt;&lt;br /&gt;    fmpq_poly_clear(x);&lt;br /&gt;    fmpq_poly_clear(w);&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;The only complication is that &lt;tt&gt;fmpq_poly_div_series&lt;/tt&gt; requires a nonzero leading coefficient in the denominator, so we must shift both series down one power.&lt;br /&gt;&lt;br /&gt;The program outputs:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;118001/2520*x^9 + 123101/5040*x^8 + 4681/360*x^7 + 283/40*x^6 + 4*x^5 + 7/3*x^4 + 3/2*x^3 + 1*x^2 + 1*x + 1&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;To make things nicer, we assume that the coefficients have the form a&lt;sub&gt;k&lt;/sub&gt; / k! (i.e. that T(z) is the exponential generating function for a&lt;sub&gt;k&lt;/sub&gt;) and change the output code to something like the following:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;    long k;&lt;br /&gt;    mpq_t t;&lt;br /&gt;    mpz_t u;&lt;br /&gt;    mpq_init(t);&lt;br /&gt;    mpz_init(u);&lt;br /&gt;&lt;br /&gt;    for (k = 0; k &lt; n; k++)&lt;br /&gt;    {&lt;br /&gt;        fmpq_poly_get_coeff_mpq(t, w, k);&lt;br /&gt;        mpz_fac_ui(u, k);&lt;br /&gt;        mpz_mul(mpq_numref(t), mpq_numref(t), u);&lt;br /&gt;        mpq_canonicalize(t);&lt;br /&gt;        gmp_printf("%Qd ", t);&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    mpq_clear(t);&lt;br /&gt;    mpz_clear(u);&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;This indeed gives us an integer sequence:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;1 1 2 9 56 480 5094 65534 984808 16992144&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Now what is the value of the 1000th coefficient (to be precise, a&lt;sub&gt;1000&lt;/sub&gt;, the initial one being the 0th!) in this sequence? After a simple modification of the program, 2.9 seconds of computation gives:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;11608872341636087705816513947297167830568265588875720061704&lt;br /&gt;01832880235304267566817912141661469362953389062004053809005&lt;br /&gt;65797054717998071778437757582562676432270594729770831984037&lt;br /&gt;17901116787718293231769568392734610884078152929278291961741&lt;br /&gt;58010228897635319982035567487202368704727403137478203768363&lt;br /&gt;54056589570878404139562784693762331122998711070595645913436&lt;br /&gt;44753733499423283972136827590268687580725109528808039530647&lt;br /&gt;10910254098110789162443473433433758060122558659258182027755&lt;br /&gt;69656436509351036076228649393400187670469063215003559774586&lt;br /&gt;49501015173633083100668758800804388616363320813332492596835&lt;br /&gt;40185987183963214465225072970422690115905543500507650640978&lt;br /&gt;08856685726892919091844572545581642428942983342505179168857&lt;br /&gt;61923031601434642410137173087273453449219217659949560840949&lt;br /&gt;29145910407919393564145312029717057693032572341514569188719&lt;br /&gt;42207889248610196901459400077483577940763454422516589494589&lt;br /&gt;38697976290832628091067571489853751119661925805775760182956&lt;br /&gt;07151657547554699411688610841404991952520564137242651305186&lt;br /&gt;19966880917401902668151574186675809680229260294868082194497&lt;br /&gt;63338464294487320831362657576767926588975644587806316363928&lt;br /&gt;21662453081804476234328933125206970873131871382852201414093&lt;br /&gt;31942812710129867491990841736391939490562342870154316209797&lt;br /&gt;95555638177793757660689621198594912024704112203014400855204&lt;br /&gt;04879191040818216462884689447945725483793082854991264186114&lt;br /&gt;00713712447555062853630274495412279277142852027491666742488&lt;br /&gt;18689076794537156576609645279481454870296442864829766014978&lt;br /&gt;76385015229773871193575960430599394232421616401025152808967&lt;br /&gt;97542967829629757402705726445239053261557399630212654678115&lt;br /&gt;91948563122399554735529747742515102962530483866618795187470&lt;br /&gt;92568029262248891738821070847168914030430887617489382116571&lt;br /&gt;31479578425767585519331805968937010542495567221591600504522&lt;br /&gt;70151935685333213987251220404383044513120115761331175072544&lt;br /&gt;91881860724844683157343078083901966247367831930705346651165&lt;br /&gt;57731933519958498663270193078704185994119446629783305199163&lt;br /&gt;25824443621182783667024174595493553934149891052564101562124&lt;br /&gt;66082538519787858297949190033471879555319648142879656530503&lt;br /&gt;22140399695072998272983889906823049155302053273484019653833&lt;br /&gt;08158019685729676988160041114485564188896445502120959889736&lt;br /&gt;26684734069125268167350474483728161637188322446040542612820&lt;br /&gt;83620649731423678182582137133666912162187578149277916758677&lt;br /&gt;65932622140692260754343559763758688544180440952477345437585&lt;br /&gt;88260535486569816885029406514351482276962081562798684604230&lt;br /&gt;27051552771077659399889469617306015354335528530235916712574&lt;br /&gt;33756257973655927835185354982512983428012895270181767297060&lt;br /&gt;61394636504681554763302758450669487653360858511886083023090&lt;br /&gt;56603401440047692698200295529572915618836122163118770906896&lt;br /&gt;63441094011689868848158568518095899683719854486361541380832&lt;br /&gt;18026233272569661209672552513531416295218659379214599386577&lt;br /&gt;71439492527626159018195922050167504883881038997644963556212&lt;br /&gt;95634222871269535245013411241216112695705600000000000000000&lt;br /&gt;0000000000000000000000000 &lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;In fact, if we look up the first 10 coefficients in the On-Line Encyclopedia of Integer Sequences, we find &lt;a href="http://oeis.org/A033917"&gt;http://oeis.org/A033917&lt;/a&gt;. This OEIS entry lists the representation&lt;br /&gt;&lt;br /&gt;a(n) = Sum_{k=0..n} Stirling1(n, k)*(k+1)^(k-1)&lt;br /&gt;&lt;br /&gt;Since FLINT supports fast vector computation of Stirling numbers, this formula can be implemented efficiently:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;#include "fmpz.h"&lt;br /&gt;#include "fmpz_vec.h"&lt;br /&gt;#include "arith.h"&lt;br /&gt;&lt;br /&gt;void coefficient(fmpz_t a, long n)&lt;br /&gt;{&lt;br /&gt;    long k;&lt;br /&gt;    fmpz * s;&lt;br /&gt;    fmpz_t t;&lt;br /&gt;&lt;br /&gt;    s = _fmpz_vec_init(n + 1);&lt;br /&gt;    fmpz_stirling1_vec(s, n, n + 1);&lt;br /&gt;&lt;br /&gt;    fmpz_init(t);&lt;br /&gt;    fmpz_zero(a);&lt;br /&gt;    for (k = 1; k &lt;= n; k++)&lt;br /&gt;    {&lt;br /&gt;        fmpz_set_ui(t, k + 1);&lt;br /&gt;        fmpz_pow_ui(t, t, k - 1);&lt;br /&gt;        fmpz_addmul(a, s + k, t);&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    _fmpz_vec_clear(s, n + 1);&lt;br /&gt;    fmpz_clear(t);&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;int main()&lt;br /&gt;{&lt;br /&gt;    fmpz_t a;&lt;br /&gt;    fmpz_init(a);&lt;br /&gt;    coefficient(a, 1000);&lt;br /&gt;    fmpz_print(a);&lt;br /&gt;    printf("\n");&lt;br /&gt;    fmpz_clear(a);&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;And indeed, the output turns out to be the same!&lt;br /&gt;&lt;br /&gt;This program is faster, taking only 0.1 seconds to run. But of course, it only gives us a single coefficient, and would be slower for computing a range of values by making repeated calls.&lt;br /&gt;&lt;br /&gt;Similar ideas to those presented here (basically, reducing a problem to fast polynomial multiplication using generating functions, Newton iteration, etc.) are used internally by FLINT for computation of the standard elementary functions themselves as well as various special numbers and polynomials (Bernoulli numbers and polynomials, partitions, Stirling numbers, Bell numbers, etc). The internal code uses a lot of tricks to reduce overhead and handle special cases faster, however. (See the previous blog post &lt;a href="http://fredrik-j.blogspot.com/2010/09/fast-combinatorial-and-number-theoretic.html"&gt;Fast combinatorial and number-theoretic functions with FLINT 2&lt;/a&gt;, and for more recent information the release announcement and benchmarks page linked at the top of this post.)&lt;br /&gt;&lt;br /&gt;In other news, I haven't written a lot of code for mpmath or Sage recently. Of course, my hope is that FLINT (2) will make it into Sage in the not too distant future. The fast polynomial and power series arithmetic support in FLINT will also be very useful for future special functions applications (in mpmath and elsewhere).&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4754734402679928849-3253966970864417650?l=fredrik-j.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://fredrik-j.blogspot.com/feeds/3253966970864417650/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=4754734402679928849&amp;postID=3253966970864417650' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4754734402679928849/posts/default/3253966970864417650'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4754734402679928849/posts/default/3253966970864417650'/><link rel='alternate' type='text/html' href='http://fredrik-j.blogspot.com/2011/03/flint-example-lambert-w-function-power.html' title='A FLINT example: Lambert W function power series'/><author><name>Fredrik Johansson</name><uri>http://www.blogger.com/profile/01465400860530971858</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4754734402679928849.post-4696399575844014395</id><published>2010-09-24T17:27:00.003+02:00</published><updated>2010-09-24T18:32:18.545+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='sage'/><category scheme='http://www.blogger.com/atom/ns#' term='sympy'/><category scheme='http://www.blogger.com/atom/ns#' term='mpmath'/><title type='text'>Announcing mpmath 0.16</title><content type='html'>I'm happy to announce the release of &lt;a href="http://code.google.com/p/mpmath/"&gt;mpmath 0.16&lt;/a&gt;, which contains the usual bugfixes as well as a slew of new features!&lt;br /&gt;&lt;br /&gt;The main focus has been to improve coverage of special functions. Additions include inhomogeneous Bessel functions, Bessel function zeros, incomplete elliptic integrals, and parabolic cylinder functions. As of 0.16, mpmath implements essentially everything listed in the &lt;a href="http://dlmf.nist.gov/"&gt;NIST Digital Library of Mathematical Functions&lt;/a&gt; chapters 1-20, as well as 21,24,27 and 33. (For 25 and 26 -- combinatorial and number-theoretic functions, see also my post about &lt;a href="http://fredrik-j.blogspot.com/2010/09/fast-combinatorial-and-number-theoretic.html"&gt;FLINT 2&lt;/a&gt;.)&lt;br /&gt;&lt;br /&gt;Another major change is that mpmath 0.16 running in &lt;a href="http://www.sagemath.org/"&gt;Sage&lt;/a&gt; will be much faster thanks to new extension code (currently awaiting review for inclusion in Sage). I've clocked speedups between 1.3x and 2x for various nontrivial pieces of code (such as the mpmath test suite and the torture test programs).&lt;br /&gt;&lt;br /&gt;Thanks to William Stein, my work on mpmath during the summer was funded using resources from &lt;a href="http://www.nsf.gov/awardsearch/showAward.do?AwardNumber=0757627&amp;version=noscript"&gt;NSF grant DMS-0757627&lt;/a&gt;. This support is gratefully acknowledged.&lt;br /&gt;&lt;br /&gt;Most of the new features are described in previous posts on this blog. For convenience, here is a short summary:&lt;br /&gt;&lt;br /&gt;&lt;a href="http://fredrik-j.blogspot.com/2010/06/assorted-special-functions-update.html"&gt;Assorted special functions update&lt;/a&gt;&lt;ul&gt;&lt;br /&gt;&lt;li&gt;The documentation now includes plots to illustrate several of the special functions.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Airy functions have been rewritten for improved speed and accuracy and to support evaluation of derivatives.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Functions &lt;tt&gt;airyaizero()&lt;/tt&gt;, &lt;tt&gt;airybizero()&lt;/tt&gt; for computation of Airy function zeros have been implemented.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Inhomogeneous Airy (Scorer) functions &lt;tt&gt;scorergi()&lt;/tt&gt; and &lt;tt&gt;scorerhi()&lt;/tt&gt; have been implemented.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Four inhomogeneous Bessel functions have been added (&lt;tt&gt;lommels1()&lt;/tt&gt;, &lt;tt&gt;lommels2()&lt;/tt&gt;, &lt;tt&gt;angerj()&lt;/tt&gt;, &lt;tt&gt;webere()&lt;/tt&gt;).&lt;/li&gt;&lt;br /&gt;&lt;li&gt;The Lambert W function has been rewritten to fix various bugs and numerical issues&lt;/li&gt;&lt;br /&gt;&lt;/ul&gt;&lt;br /&gt;&lt;a href="http://fredrik-j.blogspot.com/2010/06/incomplete-elliptic-integrals-complete.html"&gt;Incomplete elliptic integrals complete&lt;/a&gt;&lt;ul&gt;&lt;br /&gt;&lt;li&gt;The Legendre and Carlson incomplete elliptic integrals for real and complex arguments have been implemented (&lt;tt&gt;ellipf()&lt;/tt&gt;, &lt;tt&gt;ellipe()&lt;/tt&gt;, &lt;tt&gt;ellippi()&lt;/tt&gt;, &lt;tt&gt;elliprf()&lt;/tt&gt;, &lt;tt&gt;elliprc()&lt;/tt&gt;, &lt;tt&gt;elliprj()&lt;/tt&gt;, &lt;tt&gt;elliprd()&lt;/tt&gt;, &lt;tt&gt;elliprg()&lt;/tt&gt;).&lt;/li&gt;&lt;br /&gt;&lt;/ul&gt;&lt;br /&gt;&lt;a href="http://fredrik-j.blogspot.com/2010/07/sage-days-23-and-bessel-function-zeros.html"&gt;Sage Days 23, and Bessel function zeros&lt;/a&gt;&lt;br /&gt;&lt;ul&gt;&lt;br /&gt;&lt;li&gt;Functions &lt;tt&gt;besseljzero()&lt;/tt&gt; and &lt;tt&gt;besselyzero()&lt;/tt&gt; have been implemented for computing the &lt;i&gt;m&lt;/i&gt;-th zero of &lt;i&gt;J&lt;sub&gt;&amp;nu;&lt;/sub&gt;&lt;/i&gt;(&lt;i&gt;z&lt;/i&gt;), &lt;i&gt;J'&lt;sub&gt;&amp;nu;&lt;/sub&gt;&lt;/i&gt;(&lt;i&gt;z&lt;/i&gt;) &lt;i&gt;Y&lt;sub&gt;&amp;nu;&lt;/sub&gt;&lt;/i&gt;(&lt;i&gt;z&lt;/i&gt;), or &lt;i&gt;Y'&lt;sub&gt;&amp;nu;&lt;/sub&gt;&lt;/i&gt;(&lt;i&gt;z&lt;/i&gt;) for any positive integer index &lt;i&gt;m&lt;/i&gt; and real order &amp;nu; &amp;ge; 0.&lt;br /&gt;&lt;/li&gt;&lt;br /&gt;&lt;/ul&gt;&lt;br /&gt;&lt;a href="http://fredrik-j.blogspot.com/2010/07/post-sage-days-24-report.html"&gt;Post Sage Days 24 report&lt;/a&gt;&lt;br /&gt;&lt;ul&gt;&lt;br /&gt;&lt;li&gt;The Parabolic cylinder functions &lt;tt&gt;pcfd()&lt;/tt&gt;, &lt;tt&gt;pcfu()&lt;/tt&gt;, &lt;tt&gt;pcfv()&lt;/tt&gt;, &lt;tt&gt;pcfw()&lt;/tt&gt; have been implemented.&lt;/li&gt;&lt;br /&gt;&lt;/ul&gt;&lt;br /&gt;&lt;a href="http://fredrik-j.blogspot.com/2010/07/euler-maclaurin-summation-of.html"&gt;Euler-Maclaurin summation of hypergeometric series&lt;/a&gt;&lt;br /&gt;&lt;ul&gt;&lt;br /&gt;&lt;li&gt;Hypergeometric functions &lt;sub&gt;&lt;i&gt;p&lt;/i&gt;&lt;/sub&gt;&lt;i&gt;F&lt;/i&gt;&lt;sub&gt;&lt;i&gt;p&lt;/i&gt;-1&lt;/sub&gt;(...; ...; &lt;i&gt;z&lt;/i&gt;) now support accurate evaluation close to the singularity at &lt;i&gt;z&lt;/i&gt; = 1.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;A function &lt;tt&gt;sumap()&lt;/tt&gt; has been added for summation of infinite series using the Abel-Plana formula.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Functions &lt;tt&gt;diffs_prod()&lt;/tt&gt; and &lt;tt&gt;diffs_prod()&lt;/tt&gt; have been added for generating high-order derivatives of products or exponentials of functions with known derivatives.&lt;/li&gt;&lt;br /&gt;&lt;/ul&gt;&lt;br /&gt;&lt;a href="http://fredrik-j.blogspot.com/2010/09/again-mpmath-in-sage-is-about-to-get.html"&gt;Again, mpmath in Sage is about to get faster&lt;/a&gt;&lt;br /&gt;&lt;ul&gt;&lt;br /&gt;&lt;li&gt;New Cython extension code has been written for Sage to speed up various operations in mpmath, including elementary functions and hypergeometric series.&lt;/li&gt;&lt;br /&gt;&lt;/ul&gt;&lt;br /&gt;&lt;br /&gt;There are various other changes as well, such as support for matrix slice indexing (contributed by Ioannis Tziakos -- thanks!). As usual, details are available in the &lt;a href="http://mpmath.googlecode.com/svn/trunk/CHANGES"&gt;changelog&lt;/a&gt; and the &lt;a href="http://code.google.com/p/mpmath/source/list"&gt;Changes&lt;/a&gt; page on the Google Code project site.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4754734402679928849-4696399575844014395?l=fredrik-j.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://fredrik-j.blogspot.com/feeds/4696399575844014395/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=4754734402679928849&amp;postID=4696399575844014395' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4754734402679928849/posts/default/4696399575844014395'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4754734402679928849/posts/default/4696399575844014395'/><link rel='alternate' type='text/html' href='http://fredrik-j.blogspot.com/2010/09/announcing-mpmath-016.html' title='Announcing mpmath 0.16'/><author><name>Fredrik Johansson</name><uri>http://www.blogger.com/profile/01465400860530971858</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4754734402679928849.post-6858896334629563490</id><published>2010-09-22T15:20:00.006+02:00</published><updated>2010-09-22T16:42:55.899+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='sage'/><category scheme='http://www.blogger.com/atom/ns#' term='sympy'/><category scheme='http://www.blogger.com/atom/ns#' term='mpmath'/><title type='text'>Again, mpmath in Sage is about to get faster</title><content type='html'>My summer project on special functions in mpmath and Sage, generously supported by William Stein with funds from &lt;a href="http://www.nsf.gov/awardsearch/showAward.do?AwardNumber=0757627&amp;version=noscript"&gt;NSF grant DMS-0757627&lt;/a&gt;, is nearing completion. I will soon release mpmath-0.16, which contains lots of new special functions and bugfixes. Sage users will also benefit from ~1500 lines of new Cython code (preliminary patch &lt;a href="http://trac.sagemath.org/sage_trac/ticket/9969"&gt;here&lt;/a&gt;) that speeds up various basic operations. Executing &lt;tt&gt;mpmath.runtests()&lt;/tt&gt; in Sage on my laptop now takes 10.47 seconds (8.60 from a warm cache), compared to 14.21 (11.84) seconds with the new extensions disabled -- a global speedup of 30%.&lt;br /&gt;&lt;br /&gt;For comparison, pure-Python mpmath with &lt;a href="http://code.google.com/p/gmpy/"&gt;gmpy&lt;/a&gt; as the backend takes 21.46 (18.72) seconds to execute the unit tests and pure-Python mpmath with the pure-Python backend takes 52.33 (45.92) seconds.&lt;br /&gt;&lt;br /&gt;Specifically, the new extension code implements exp for real and complex arguments, cos, sin and ln for real arguments, complex exponentiation in some cases, and summation of hypergeometric series, entirely in Cython.&lt;br /&gt;&lt;br /&gt;Timings before (new extensions disabled):&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;sage: import mpmath&lt;br /&gt;sage: x = mpmath.mpf(0.37)&lt;br /&gt;sage: y = mpmath.mpf(0.49)&lt;br /&gt;sage: %timeit mpmath.exp(x)&lt;br /&gt;625 loops, best of 3: 14.5 µs per loop&lt;br /&gt;sage: %timeit mpmath.ln(x)&lt;br /&gt;625 loops, best of 3: 23.2 µs per loop&lt;br /&gt;sage: %timeit mpmath.cos(x)&lt;br /&gt;625 loops, best of 3: 17.2 µs per loop&lt;br /&gt;sage: %timeit x ^ y&lt;br /&gt;625 loops, best of 3: 39.9 µs per loop&lt;br /&gt;sage: %timeit mpmath.hyp1f1(2r,3r,4r)&lt;br /&gt;625 loops, best of 3: 90.3 µs per loop&lt;br /&gt;sage: %timeit mpmath.hyp1f1(x,y,x)&lt;br /&gt;625 loops, best of 3: 83.6 µs per loop&lt;br /&gt;sage: %timeit mpmath.hyp1f1(x,y,mpmath.mpc(x,y))&lt;br /&gt;625 loops, best of 3: 136 µs per loop&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Timings after (new extensions enabled):&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;sage: import mpmath&lt;br /&gt;sage: x = mpmath.mpf(0.37)&lt;br /&gt;sage: y = mpmath.mpf(0.49)&lt;br /&gt;sage: %timeit mpmath.exp(x)&lt;br /&gt;625 loops, best of 3: 2.72 µs per loop&lt;br /&gt;sage: %timeit mpmath.ln(x)&lt;br /&gt;625 loops, best of 3: 7.25 µs per loop&lt;br /&gt;sage: %timeit mpmath.cos(x)&lt;br /&gt;625 loops, best of 3: 4.13 µs per loop&lt;br /&gt;sage: %timeit x ^ y&lt;br /&gt;625 loops, best of 3: 10.5 µs per loop&lt;br /&gt;sage: %timeit mpmath.hyp1f1(2r,3r,4r)&lt;br /&gt;625 loops, best of 3: 47.1 µs per loop&lt;br /&gt;sage: %timeit mpmath.hyp1f1(x,y,x)&lt;br /&gt;625 loops, best of 3: 59.4 µs per loop&lt;br /&gt;sage: %timeit mpmath.hyp1f1(x,y,mpmath.mpc(x,y))&lt;br /&gt;625 loops, best of 3: 83.1 µs per loop&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;The new elementary functions use a combination of custom algorithms and straightforward &lt;a href="http://www.mpfr.org/"&gt;MPFR&lt;/a&gt; wrappers. Why not just wrap MPFR for everything? There are two primary reasons:&lt;br /&gt;&lt;br /&gt;Firstly, because MPFR numbers have a limited range, custom code still needs to be used in the overflowing cases, and this is almost as much work as an implementation-from-scratch. (There are also some more minor incompatibilities, like lack of round-away-from-zero in MPFR, that result in a lot of extra work.)&lt;br /&gt;&lt;br /&gt;Secondly, MPFR is not always fast (or as fast as it could be), so it pays off to write custom code. In fact, some of the ordinary Python implementations of functions in mpmath are faster than their MPFR counterparts in various cases, although that is rather exceptional (atan is an example). But generally, at low-mid precisions, it is possible to be perhaps 2-4x faster than MPFR with carefully optimized C code (see &lt;a href="http://code.google.com/p/fastfunlib/"&gt;fastfunlib&lt;/a&gt;). This is a longer-term goal.&lt;br /&gt;&lt;br /&gt;Already now, with the new extension code, the mpmath exponential function becomes faster than the Sage RealNumber version (based on MPFR) at low precision:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;sage: %timeit mpmath.exp(x)&lt;br /&gt;625 loops, best of 3: 2.75 µs per loop&lt;br /&gt;sage: w = RealField(53)(x)&lt;br /&gt;sage: %timeit w.exp()&lt;br /&gt;625 loops, best of 3: 5.57 µs per loop&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;As the timings above indicate, hypergeometric series have gotten up to 2x faster. The speedup of the actual summation is much larger, but much of that gain is lost in various Python overheads (more work can be done on this). There should be a noticeable speedup for some hypergeometric function computations, while others will not benefit as much, for the moment.&lt;br /&gt;&lt;br /&gt;Another benchmark is the &lt;tt&gt;extratest_zeta.py&lt;/tt&gt; script in mpmath, which exercises the mpmath implementation of the &lt;a href="http://en.wikipedia.org/wiki/Riemann%E2%80%93Siegel_formula"&gt;Riemann-Siegel formula&lt;/a&gt; for evaluation of &amp;zeta;(&lt;i&gt;s&lt;/i&gt;) for complex &lt;i&gt;s&lt;/i&gt; with large imaginary part. Such computations largely depend on elementary function performance (cos, sin, exp, log).&lt;br /&gt;&lt;br /&gt;Here are the new timings for mpmath in Sage: &lt;pre&gt;&lt;br /&gt;fredrik@scv:~/sage$ ./sage /home/fredrik/mp/mpmath/tests/extratest_zeta.py&lt;br /&gt;399999999 156762524.675 ok = True (time = 1.144)&lt;br /&gt;241389216 97490234.2277 ok = True (time = 9.271)&lt;br /&gt;526196239 202950727.691 ok = True (time = 1.671)&lt;br /&gt;542964976 209039046.579 ok = True (time = 1.189)&lt;br /&gt;1048449112 388858885.231 ok = True (time = 1.774)&lt;br /&gt;1048449113 388858885.384 ok = True (time = 1.604)&lt;br /&gt;1048449114 388858886.002 ok = True (time = 2.096)&lt;br /&gt;1048449115 388858886.002 ok = True (time = 2.587)&lt;br /&gt;1048449116 388858886.691 ok = True (time = 1.546)&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;This is mpmath in Sage with the new extension code disabled: &lt;pre&gt;&lt;br /&gt;fredrik@scv:~/sage$ ./sage /home/fredrik/mp/mpmath/tests/extratest_zeta.py&lt;br /&gt;399999999 156762524.675 ok = True (time = 2.352)&lt;br /&gt;241389216 97490234.2277 ok = True (time = 14.088)&lt;br /&gt;526196239 202950727.691 ok = True (time = 3.036)&lt;br /&gt;542964976 209039046.579 ok = True (time = 2.104)&lt;br /&gt;1048449112 388858885.231 ok = True (time = 3.707)&lt;br /&gt;1048449113 388858885.384 ok = True (time = 3.283)&lt;br /&gt;1048449114 388858886.002 ok = True (time = 4.444)&lt;br /&gt;1048449115 388858886.002 ok = True (time = 5.592)&lt;br /&gt;1048449116 388858886.691 ok = True (time = 3.101)&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;This is mpmath in ordinary Python mode, using gmpy: &lt;pre&gt;&lt;br /&gt;fredrik@scv:~/sage$ python /home/fredrik/mp/mpmath/tests/extratest_zeta.py&lt;br /&gt;399999999 156762524.675 ok = True (time = 2.741)&lt;br /&gt;241389216 97490234.2277 ok = True (time = 13.842)&lt;br /&gt;526196239 202950727.691 ok = True (time = 3.124)&lt;br /&gt;542964976 209039046.579 ok = True (time = 2.143)&lt;br /&gt;1048449112 388858885.231 ok = True (time = 3.257)&lt;br /&gt;1048449113 388858885.384 ok = True (time = 2.912)&lt;br /&gt;1048449114 388858886.002 ok = True (time = 3.953)&lt;br /&gt;1048449115 388858886.002 ok = True (time = 4.964)&lt;br /&gt;1048449116 388858886.691 ok = True (time = 2.762)&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;With the new extension code, it appears that zeta computations are up to about twice as fast. This speedup could be made much larger as there still is a significant amount of Python overhead left to remove -- also a project for the future.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4754734402679928849-6858896334629563490?l=fredrik-j.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://fredrik-j.blogspot.com/feeds/6858896334629563490/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=4754734402679928849&amp;postID=6858896334629563490' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4754734402679928849/posts/default/6858896334629563490'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4754734402679928849/posts/default/6858896334629563490'/><link rel='alternate' type='text/html' href='http://fredrik-j.blogspot.com/2010/09/again-mpmath-in-sage-is-about-to-get.html' title='Again, mpmath in Sage is about to get faster'/><author><name>Fredrik Johansson</name><uri>http://www.blogger.com/profile/01465400860530971858</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4754734402679928849.post-5343369310477112221</id><published>2010-09-05T16:50:00.004+02:00</published><updated>2010-09-05T21:38:55.735+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='sage'/><category scheme='http://www.blogger.com/atom/ns#' term='sympy'/><category scheme='http://www.blogger.com/atom/ns#' term='mpmath'/><category scheme='http://www.blogger.com/atom/ns#' term='flint'/><title type='text'>Fast combinatorial and number-theoretic functions with FLINT 2</title><content type='html'>Time for a development update! Recently, I've done only a limited amount of work on mpmath (I have a some almost-finished Cython code for &lt;tt&gt;sage.libs.mpmath&lt;/tt&gt; and new code for numerical integration in mpmath, both to be committed fairly soon -- within a couple of weeks, hopefully).&lt;br /&gt;&lt;br /&gt;The last few weeks, I've mostly been contributing to &lt;a href="http://www.flintlib.org/"&gt;FLINT 2&lt;/a&gt;. For those unfamiliar with it, FLINT is a fast C library for computational number theory developed by Bill Hart and others (the other active developers right now are Sebastian Pancratz and Andy Novocin). In particular, FLINT implements ridiculously fast multiprecision integer vectors and polynomials. It also provides very fast primality testing and factorization for word-size integers (32 or 64 bits), among other things. FLINT 2 is an in-progress rewrite of FLINT 1.x, a current standard component in Sage.&lt;br /&gt;&lt;br /&gt;What does this have to do with numerical evaluation of special functions (the usual theme of this blog)? In short, my goal is to add code to FLINT 2 for &lt;i&gt;exact&lt;/i&gt; special function computations -- combinatorial and number-theoretic functions, special polynomials and the like. Such functions benefit tremendously from the fast integer and polynomial arithmetic available in FLINT 2.&lt;br /&gt;&lt;br /&gt;All my code can be found in my &lt;a href="http://github.com/fredrik-johansson/flint2/"&gt;public GitHub repository&lt;/a&gt; (the most recent commits as of this writing are in the 'factor' branch).&lt;br /&gt;&lt;br /&gt;Functions I've implemented so far include:&lt;br /&gt;&lt;ul&gt;&lt;br /&gt;&lt;li&gt;Möbius &amp;mu; and Euler &amp;phi; (totient) functions for word-size and arbitrary-size integers&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Divisor sum function &amp;sigma;&lt;sub&gt;&lt;i&gt;k&lt;/i&gt;&lt;/sub&gt; for arbitrary-size integers&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Ramanujan &amp;tau; function (&amp;Delta;-function &lt;i&gt;q&lt;/i&gt;-expansion)&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Harmonic numbers 1 + 1/2 + 1/3 + ... + 1/&lt;i&gt;n&lt;/i&gt;&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Primorials 2 &amp;middot; 3 &amp;middot; 5 &amp;middot; ... &amp;middot; &lt;i&gt;p&lt;/i&gt;&lt;sub&gt;&lt;i&gt;n&lt;/i&gt;&lt;/sub&gt;&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Stirling numbers (1st and 2nd kind)&lt;/li&gt;&lt;br /&gt;&lt;/ul&gt;&lt;br /&gt;&lt;br /&gt;The versions in FLINT 2 of these functions should now be faster than all other implementations I've tried (GAP, Pari, Mathematica, the Sage library) for all ranges of arguments, except for those requiring factorization of large integers.&lt;br /&gt;&lt;br /&gt;Some of these functions depend fundamentally on the ability to factorize integers efficiently. So far I've only implemented trial division for large integers in FLINT 2, with some clever code to extract large powers of small factors quickly. Sufficiently small cofactors are handled by calling Bill Hart's single-word factoring routines. The resulting code is very fast for "artificial" numbers like factorials, and will eventually be complemented with prime and perfect power detection code, plus fast implementations of Brent's algorithm and other methods. Later on the quadratic sieve from FLINT 1 will probably be ported to FLINT 2, so that FLINT 2 will be able to factor any reasonable number reasonably quickly.&lt;br /&gt;&lt;br /&gt;Below, I've posted some benchmark results. A word of caution: all Mathematica timings were done on a different system, which is faster than my own laptop (typically by 30% or so). So in reality, Mathematica performs slightly worse relatively than indicated below. Everything else is timed on my laptop. I have not included test code for the FLINT2 functions (but it's just straightforward C code -- a function call or two between &lt;tt&gt;timeit_start&lt;/tt&gt; and &lt;tt&gt;timeit_stop&lt;/tt&gt; using FLINT 2's profiler module).&lt;br /&gt;&lt;br /&gt;Möbius function (the following is basically a raw exercise of the small-integer factoring code):&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;Pari:&lt;br /&gt;sage: %time pari('sum(n=1,10^6,moebius(n))');&lt;br /&gt;CPU times: user 1.04 s, sys: 0.00 s, total: 1.04 s&lt;br /&gt;Wall time: 1.04 s&lt;br /&gt;&lt;br /&gt;Mathematica:&lt;br /&gt;In[1]:= Timing[Sum[MoebiusMu[n], {n,1,10^6}];]&lt;br /&gt;Out[1]= {0.71, Null}&lt;br /&gt;&lt;br /&gt;flint2:&lt;br /&gt;650 ms&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Divisor sum:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;Sage (uses Cython code):&lt;br /&gt;sage: %time sigma(factorial(1000),1000);&lt;br /&gt;CPU times: user 0.47 s, sys: 0.00 s, total: 0.47 s&lt;br /&gt;Wall time: 0.46 s&lt;br /&gt;&lt;br /&gt;Mathematica:&lt;br /&gt;In[1]:= Timing[DivisorSigma[1000,1000!];]&lt;br /&gt;Out[1]= {3.01, Null}&lt;br /&gt;&lt;br /&gt;flint2:&lt;br /&gt;350 ms&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Ramanujan &amp;tau; function:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;Sage (uses FLINT 1):&lt;br /&gt;sage: %time delta_qexp(100000);&lt;br /&gt;CPU times: user 0.42 s, sys: 0.01 s, total: 0.43 s&lt;br /&gt;Wall time: 0.42 s&lt;br /&gt;sage: %time delta_qexp(1000000);&lt;br /&gt;CPU times: user 6.02 s, sys: 0.37 s, total: 6.39 s&lt;br /&gt;Wall time: 6.40 s&lt;br /&gt;&lt;br /&gt;flint2:&lt;br /&gt;100000: 230 ms&lt;br /&gt;1000000: 4500 ms&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;An isolated value (Mathematica seems to be the only other software that knows how to compute this):&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;Mathematica:&lt;br /&gt;In[1]:= Timing[RamanujanTau[10000!];]&lt;br /&gt;Out[1]= {8.74, Null}&lt;br /&gt;&lt;br /&gt;flint2:&lt;br /&gt;280 ms&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Harmonic numbers (again, only Mathematica seems to implement these). See also my old blog post &lt;a href="http://fredrik-j.blogspot.com/2009/02/how-not-to-compute-harmonic-numbers.html"&gt;How (not) to compute harmonic numbers&lt;/a&gt;. I've included the fastest version from there, harmonic5:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;Mathematica:&lt;br /&gt;In[1]:= Timing[HarmonicNumber[100000];]&lt;br /&gt;Out[1]= {0.22, Null}&lt;br /&gt;In[2]:= Timing[HarmonicNumber[1000000];]&lt;br /&gt;Out[2]= {6.25, Null}&lt;br /&gt;In[3]:= Timing[HarmonicNumber[10000000];]&lt;br /&gt;Out[3]= {129.13, Null}&lt;br /&gt;&lt;br /&gt;harmonic5: (100000):&lt;br /&gt;100000: 0.471 s&lt;br /&gt;1000000: 8.259 s&lt;br /&gt;10000000: 143.639 s&lt;br /&gt;&lt;br /&gt;flint2:&lt;br /&gt;100000: 100 ms&lt;br /&gt;1000000: 2560 ms&lt;br /&gt;10000000: 49400 ms&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;The FLINT 2 function benefits from an improved algorithm that eliminates terms and reduces the size of the temporary numerators and denominators, as well as low-level optimization (the basecase summation directly uses the MPIR mpn interface).&lt;br /&gt;&lt;br /&gt;Isolated Stirling numbers of the first kind:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;Mathematica:&lt;br /&gt;In[1]:= Timing[StirlingS1[1000,500];]&lt;br /&gt;Out[1]= {0.24, Null}&lt;br /&gt;In[2]:= Timing[StirlingS1[2000,1000];]&lt;br /&gt;Out[2]= {1.79, Null}&lt;br /&gt;In[3]:= Timing[StirlingS1[3000,1500];]&lt;br /&gt;Out[3]= {5.13, Null}&lt;br /&gt;&lt;br /&gt;flint 2:&lt;br /&gt;100,500: 100 ms&lt;br /&gt;2000,1000: 740 ms&lt;br /&gt;3000,1500: 1520 ms&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Isolated Stirling numbers of the second kind:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;Mathematica:&lt;br /&gt;In[1]:= Timing[StirlingS2[1000,500];]&lt;br /&gt;Out11]= {0.21, Null}&lt;br /&gt;In[2]:= Timing[StirlingS2[2000,1000];]&lt;br /&gt;Out[2]= {1.54, Null}&lt;br /&gt;In[3]:= Timing[StirlingS2[3000,1500];]&lt;br /&gt;Out[3]= {4.55, Null}&lt;br /&gt;In[4]:= Timing[StirlingS2[5000,2500];]&lt;br /&gt;Out[4]= {29.25, Null}&lt;br /&gt;&lt;br /&gt;flint2:&lt;br /&gt;1000,500: 2 ms&lt;br /&gt;2000,1000: 17 ms&lt;br /&gt;3000,1500: 50 ms&lt;br /&gt;5000,2500: 240 ms&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;In addition, fast functions are provided for computing a whole row or matrix of Stirling numbers. For example, computing the triangular matrix of ~1.1 million Stirling numbers of the first kind up to S(1500,1500) takes only 1.3 seconds. In Mathematica (again, on the faster system):&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;In[1]:= Timing[Table[StirlingS1[n,k], {n,0,1500}, {k,0,n}];]&lt;br /&gt;Out[1]= {2.13, Null}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;The benchmarks above mostly demonstrate performance for large inputs. Another nice aspect of the FLINT 2 functions is that there typically is very little overhead for small inputs. The high performance is due to a combination of algorithms, low-level optimization, and (most importantly) the fast underlying arithmetic in FLINT 2. I will perhaps write some more about the algorithms (for e.g. Stirling numbers) in a later post.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4754734402679928849-5343369310477112221?l=fredrik-j.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://fredrik-j.blogspot.com/feeds/5343369310477112221/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=4754734402679928849&amp;postID=5343369310477112221' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4754734402679928849/posts/default/5343369310477112221'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4754734402679928849/posts/default/5343369310477112221'/><link rel='alternate' type='text/html' href='http://fredrik-j.blogspot.com/2010/09/fast-combinatorial-and-number-theoretic.html' title='Fast combinatorial and number-theoretic functions with FLINT 2'/><author><name>Fredrik Johansson</name><uri>http://www.blogger.com/profile/01465400860530971858</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4754734402679928849.post-8421138063566161330</id><published>2010-07-28T21:23:00.009+02:00</published><updated>2010-08-04T15:27:53.246+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='sage'/><category scheme='http://www.blogger.com/atom/ns#' term='sympy'/><category scheme='http://www.blogger.com/atom/ns#' term='mpmath'/><title type='text'>Euler-Maclaurin summation of hypergeometric series</title><content type='html'>I recently fixed another corner case (it's always the corner cases that are hard!) in the hypergeometric function evaluation code in mpmath.&lt;br /&gt;&lt;br /&gt;The difficulty in question concerns the functions &lt;sub&gt;&lt;i&gt;p&lt;/i&gt;&lt;/sub&gt;&lt;i&gt;F&lt;/i&gt;&lt;sub&gt;&lt;i&gt;p&lt;/i&gt;-1&lt;/sub&gt;(...; ...; &lt;i&gt;z&lt;/i&gt;), of which the Gauss hypergeometric function &lt;sub&gt;2&lt;/sub&gt;&lt;i&gt;F&lt;/i&gt;&lt;sub&gt;1&lt;/sub&gt; is an important special case. These functions can be thought of as generalizations of the geometric series&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_rh0QblLk0C0/TFCIHTI0lEI/AAAAAAAAAUQ/j-LsprfzhkE/s1600/h3.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 227px; height: 48px;" src="http://4.bp.blogspot.com/_rh0QblLk0C0/TFCIHTI0lEI/AAAAAAAAAUQ/j-LsprfzhkE/s400/h3.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5499044803997111362" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;and the natural logarithm&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://1.bp.blogspot.com/_rh0QblLk0C0/TFCHMAB6e3I/AAAAAAAAAUI/pOUKOM2qE_c/s1600/h2.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 341px; height: 50px;" src="http://1.bp.blogspot.com/_rh0QblLk0C0/TFCHMAB6e3I/AAAAAAAAAUI/pOUKOM2qE_c/s400/h2.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5499043785255582578" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;and share the property that the hypergeometric (Maclaurin) series has radius of convergence 1, with a singularity (either algebraic or logarithmic, as above) at &lt;i&gt;z&lt;/i&gt; = 1.&lt;br /&gt;&lt;br /&gt;Numerical evaluation is fairly easy for &lt;i&gt;z&lt;/i&gt; in most of the complex plane. The series for &lt;sub&gt;&lt;i&gt;p&lt;/i&gt;&lt;/sub&gt;&lt;i&gt;F&lt;/i&gt;&lt;sub&gt;&lt;i&gt;p&lt;/i&gt;-1&lt;/sub&gt;(...; ...; &lt;i&gt;z&lt;/i&gt;) converges like the geometric series, so direct summation works well for, say, |&lt;i&gt;z&lt;/i&gt;| &amp;le; &lt;i&gt;r&lt;/i&gt; 0.9. Likewise, there is an analytic continuation formula which transforms &lt;i&gt;z&lt;/i&gt; to 1/&lt;i&gt;z&lt;/i&gt;, allowing evaluation for (say) |&lt;i&gt;z&lt;/i&gt;| &amp;ge; 1.1.&lt;br /&gt;&lt;br /&gt;The remaining shell around the unit circle 0.9 &lt; |&lt;i&gt;z&lt;/i&gt;| &lt; 1.1 is the difficult region. In fact, &lt;sub&gt;2&lt;/sub&gt;&lt;i&gt;F&lt;/i&gt;&lt;sub&gt;1&lt;/sub&gt; can still (essentially) be computed using exact transformations here, but the higher cases remain difficult. As mentioned in an &lt;a href="http://fredrik-j.blogspot.com/2009/12/analytic-continuation-of-3f2-4f3-and.html"&gt;earlier post&lt;/a&gt;, mpmath handles this by calling &lt;tt&gt;nsum()&lt;/tt&gt; which applies convergence acceleration to the slowly converging series. In particular, &lt;tt&gt;nsum&lt;/tt&gt; implements the (generalized) &lt;a href="http://en.wikipedia.org/wiki/Shanks_transformation"&gt;Shanks transformation&lt;/a&gt; which is extremely efficient for this particular type of series &amp;ndash; in fact, it works outside the radius of convergence (Shanks transformation effectively computes a Padé approximant), so it can be used anywhere in the difficult shell.&lt;br /&gt;&lt;br /&gt;Still, the Shanks transformation is most efficient when arg(&lt;i&gt;z&lt;/i&gt;) = &amp;plusmn;&amp;pi; and unfortunately degenerates as arg(&lt;i&gt;z&lt;/i&gt;) &amp;rarr; 0, i.e. close to &lt;i&gt;z&lt;/i&gt; = 1. &lt;tt&gt;nsum&lt;/tt&gt; also implements &lt;a href="http://en.wikipedia.org/wiki/Richardson_extrapolation"&gt;Richardson extrapolation&lt;/a&gt;, which sometimes works at &lt;i&gt;z&lt;/i&gt; = 1, but only for special parameter combinations in the hypergeometric series (although those special combinations happen to be quite important &amp;ndash; they include the case where the hypergeometric series reduces to a polygamma function or polylogarithm).&lt;br /&gt;&lt;br /&gt;Thus, we have the following evaluation strategy for &lt;sub&gt;&lt;i&gt;p&lt;/i&gt;&lt;/sub&gt;&lt;i&gt;F&lt;/i&gt;&lt;sub&gt;&lt;i&gt;p&lt;/i&gt;-1&lt;/sub&gt;:&lt;br /&gt;&lt;br /&gt;Blue: direct summation (with respect to &lt;i&gt;z&lt;/i&gt; or 1/&lt;i&gt;z&lt;/i&gt;)&lt;br /&gt;Yellow: Shanks transformation&lt;br /&gt;Red: ???&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://2.bp.blogspot.com/_rh0QblLk0C0/TFImLM1V3pI/AAAAAAAAAUY/al-aaPxHOPw/s1600/circle.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 300px; height: 300px;" src="http://2.bp.blogspot.com/_rh0QblLk0C0/TFImLM1V3pI/AAAAAAAAAUY/al-aaPxHOPw/s400/circle.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5499500068838170258" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;A commenter &lt;a href="http://fredrik-j.blogspot.com/2009/12/analytic-continuation-of-3f2-4f3-and.html?showComment=1274751298704#c5051305326286671188"&gt;suggested&lt;/a&gt; trying some other convergence acceleration techniques (like the Levin transformation). But it seems that they still will fail in some cases.&lt;br /&gt;&lt;br /&gt;Thus I've finally implemented the &lt;a href="http://en.wikipedia.org/wiki/Euler%E2%80%93Maclaurin_formula"&gt;Euler-Maclaurin summation formula&lt;/a&gt; for the remaining case. The E-M formula differs from other convergence acceleration techniques in that it is based on analyticity of the integrand and does not require a particular rate of convergence. It is implemented in mpmath as &lt;tt&gt;sumem()&lt;/tt&gt;, but requires a little work to apply efficiently.&lt;br /&gt;&lt;br /&gt;To apply the E-M formula to a hypergeometric series &amp;Sigma; &lt;i&gt;T&lt;/i&gt;(&lt;i&gt;k&lt;/i&gt;), the term &lt;i&gt;T&lt;/i&gt;(&lt;i&gt;k&lt;/i&gt;) must first of all obviously be extended to an analytic function of &lt;i&gt;k&lt;/i&gt;, which is done the straightforward way by considering the rising factorials as quotients of gamma functions &lt;i&gt;a&lt;/i&gt;(&lt;i&gt;a&lt;/i&gt;+1)...(&lt;i&gt;a&lt;/i&gt;+&lt;i&gt;k&lt;/i&gt;&amp;minus;1) = &amp;Gamma;(&lt;i&gt;a&lt;/i&gt;+&lt;i&gt;k&lt;/i&gt;) / &amp;Gamma;(&lt;i&gt;a&lt;/i&gt;).&lt;br /&gt;&lt;br /&gt;We are left with the difficulty of having to integrate &lt;i&gt;T&lt;/i&gt;(&lt;i&gt;k&lt;/i&gt;) and also to compute &lt;i&gt;n&lt;/i&gt;-th order derivatives of this function for the tail expansion in the E-M formula (neither of which can be done in closed form). Fortunately, numerical integration with &lt;tt&gt;quad()&lt;/tt&gt; works very well, but the derivatives remain a problem.&lt;br /&gt;&lt;br /&gt;By default, &lt;tt&gt;sumem()&lt;/tt&gt; computes the derivatives using finite differences. Although this works, it gets extremely expensive at high precision for hypergeometric series due to the rapidly increasing cost of evaluating the gamma function as the precision increases. But &lt;i&gt;T&lt;/i&gt;(&lt;i&gt;k&lt;/i&gt;) is a product of functions that &lt;i&gt;individually&lt;/i&gt; can be differentiated logarithmically in closed form (in terms of polygamma functions). I therefore implemented the equivalent of symbolic high-order differentiation for products and the exponential function using generators.&lt;br /&gt;&lt;br /&gt;&lt;tt&gt;diffs_prod&lt;/tt&gt; generates the high-order derivatives of a product, given generators for products of individual factors:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;&gt;&gt;&gt; f = lambda x: exp(x)*cos(x)*sin(x)&lt;br /&gt;&gt;&gt;&gt; u = diffs(f, 1)&lt;br /&gt;&gt;&gt;&gt; v = diffs_prod([diffs(exp,1), diffs(cos,1), diffs(sin,1)])&lt;br /&gt;&gt;&gt;&gt; next(u); next(v)&lt;br /&gt;1.23586333600241&lt;br /&gt;1.23586333600241&lt;br /&gt;&gt;&gt;&gt; next(u); next(v)&lt;br /&gt;0.104658952245596&lt;br /&gt;0.104658952245596&lt;br /&gt;&gt;&gt;&gt; next(u); next(v)&lt;br /&gt;-5.96999877552086&lt;br /&gt;-5.96999877552086&lt;br /&gt;&gt;&gt;&gt; next(u); next(v)&lt;br /&gt;-12.4632923122697&lt;br /&gt;-12.4632923122697&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;tt&gt;diffs_exp&lt;/tt&gt; generates the high-order derivatives of exp(&lt;i&gt;f&lt;/i&gt;(&lt;i&gt;x&lt;/i&gt;)), given a generator for the derivatives of &lt;i&gt;f&lt;/i&gt;(&lt;i&gt;x&lt;/i&gt;):&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;&gt;&gt;&gt; def diffs_loggamma(x):&lt;br /&gt;...     yield loggamma(x)&lt;br /&gt;...     i = 0&lt;br /&gt;...     while 1:&lt;br /&gt;...         yield psi(i,x)&lt;br /&gt;...         i += 1&lt;br /&gt;...&lt;br /&gt;&gt;&gt;&gt; u = diffs_exp(diffs_loggamma(3))&lt;br /&gt;&gt;&gt;&gt; v = diffs(gamma, 3)&lt;br /&gt;&gt;&gt;&gt; next(u); next(v)&lt;br /&gt;2.0&lt;br /&gt;2.0&lt;br /&gt;&gt;&gt;&gt; next(u); next(v)&lt;br /&gt;1.84556867019693&lt;br /&gt;1.84556867019693&lt;br /&gt;&gt;&gt;&gt; next(u); next(v)&lt;br /&gt;2.49292999190269&lt;br /&gt;2.49292999190269&lt;br /&gt;&gt;&gt;&gt; next(u); next(v)&lt;br /&gt;3.44996501352367&lt;br /&gt;3.44996501352367&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Actually only differentiation of the exponential function is necessary, which I didn't realize at first, but the product differentiation is still a nice feature to have.&lt;br /&gt;&lt;br /&gt;Exponential differentiation amounts to constructing a polynomial of &lt;i&gt;n&lt;/i&gt;+1 variables (the derivatives of &lt;i&gt;f&lt;/i&gt;), which has &lt;i&gt;P&lt;/i&gt;(&lt;i&gt;n&lt;/i&gt;) (partition function) &amp;asymp; exp(&amp;radic;&lt;i&gt;n&lt;/i&gt;) terms. Despite the rapid growth, it is indeed faster to use this approach to differentiate gamma function products at high precision, since the precision can be kept constant. In fact, this should even work in double precision (&lt;tt&gt;fp&lt;/tt&gt; arithmetic) although I think it's broken right now due to some minor bug.&lt;br /&gt;&lt;br /&gt;As a result of all this, here are two examples that now work (the results have full accuracy):&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;&gt;&gt;&gt; mp.dps = 25&lt;br /&gt;&gt;&gt;&gt; hyper(['1/3',1,'3/2',2], ['1/5','11/6','41/8'], 1)&lt;br /&gt;2.219433352235586121250027&lt;br /&gt;&lt;br /&gt;&gt;&gt;&gt; hyper(['1/3',1,'3/2',2], ['1/5','11/6','5/4'], 1)&lt;br /&gt;+inf&lt;br /&gt;&gt;&gt;&gt; eps1 = extradps(6)(lambda: 1 - mpf('1e-6'))()&lt;br /&gt;&gt;&gt;&gt; hyper(['1/3',1,'3/2',2], ['1/5','11/6','5/4'], eps1)&lt;br /&gt;2923978034.412973409330956&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;h4&gt;Application: roots of polynomials&lt;/h4&gt;&lt;br /&gt;&lt;br /&gt;A neat application is to compute the roots of quintic polynomials. Such roots can be expressed in closed form using hypergeometric functions, as outlined in the Wikipedia article &lt;a href="http://en.wikipedia.org/wiki/Bring_radical"&gt;Bring radical&lt;/a&gt;. This is more interesting in theory than practice, since polynomial roots can be calculated very efficiently using iterative methods.&lt;br /&gt;&lt;br /&gt;In particular, the roots of &lt;i&gt;x&lt;/i&gt;&lt;sup&gt;5&lt;/sup&gt; - &lt;i&gt;x&lt;/i&gt; + &lt;i&gt;t&lt;/i&gt; are given (in some order) by&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_rh0QblLk0C0/TFlkH0bljiI/AAAAAAAAAUk/X7Tl8d54GH4/s1600/1f2a0c3edbd9e10fdd6a6aa5e1c33fe9.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 400px; height: 107px;" src="http://4.bp.blogspot.com/_rh0QblLk0C0/TFlkH0bljiI/AAAAAAAAAUk/X7Tl8d54GH4/s400/1f2a0c3edbd9e10fdd6a6aa5e1c33fe9.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5501538505305984546" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;where&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://1.bp.blogspot.com/_rh0QblLk0C0/TFlkIFqd6yI/AAAAAAAAAUs/UZUhNW2xwdg/s1600/a28bdaf32bfb6fb82ec5e4f98feb7c7e.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 400px; height: 96px;" src="http://1.bp.blogspot.com/_rh0QblLk0C0/TFlkIFqd6yI/AAAAAAAAAUs/UZUhNW2xwdg/s400/a28bdaf32bfb6fb82ec5e4f98feb7c7e.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5501538509931801378" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Just verifying one case:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;&gt;&gt;&gt; mp.dps = 25; mp.pretty = True&lt;br /&gt;&gt;&gt;&gt; t = mpf(3)&lt;br /&gt;&gt;&gt;&gt; for r in polyroots([1,0,0,0,-1,t]):&lt;br /&gt;...     print r&lt;br /&gt;... &lt;br /&gt;-1.341293531690699250693537&lt;br /&gt;(0.9790618691253385511579325 - 0.6252190807348055061205923j)&lt;br /&gt;(0.9790618691253385511579325 + 0.6252190807348055061205923j)&lt;br /&gt;(-0.3084151032799889258111639 + 1.249926913731033699905407j)&lt;br /&gt;(-0.3084151032799889258111639 - 1.249926913731033699905407j)&lt;br /&gt;&gt;&gt;&gt; -t*hyper(['1/5','2/5','3/5','4/5'], ['1/2','3/4','5/4'], 3125*t**4/256)&lt;br /&gt;(-0.9790618691253385511579325 + 0.6252190807348055061205923j)&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Now, we can choose &lt;i&gt;t&lt;/i&gt; such that the hypergeometric argument becomes unity:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;&gt;&gt;&gt; t = root(mpf(256)/3125,4)&lt;br /&gt;&gt;&gt;&gt; for r in polyroots([1,0,0,0,-1,t]):&lt;br /&gt;...     print r&lt;br /&gt;... &lt;br /&gt;-1.103842268886161371052433&lt;br /&gt;(0.6687403049764220240032331 + 3.167963942396665205386494e-14j)&lt;br /&gt;(0.6687403049764220240032331 - 3.172324181973539074536523e-14j)&lt;br /&gt;(-0.1168191705333413384770166 + 1.034453571405662514459057j)&lt;br /&gt;(-0.1168191705333413384770166 - 1.034453571405662514459057j)&lt;br /&gt;&lt;br /&gt;&gt;&gt;&gt; -t*hyper(['1/5','2/5','3/5','4/5'], ['1/2','3/4','5/4'], 1)&lt;br /&gt;-0.6687403049764220240032331&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;The last line would previously fail.&lt;br /&gt;&lt;br /&gt;Interesting to note is that the value at the singular point &lt;i&gt;z&lt;/i&gt; = 1 corresponds to a double root. This also makes &lt;tt&gt;polyroots&lt;/tt&gt; return inaccurate values (note the imaginary parts), a known deficiency that should be fixed...&lt;br /&gt;&lt;br /&gt;&lt;h4&gt;Better methods&lt;/h4&gt;&lt;br /&gt;&lt;br /&gt;In fact, there may be better methods than the Euler-Maclaurin formula. One such approach is to use the &lt;a href="http://mathworld.wolfram.com/Abel-PlanaFormula.html"&gt;Abel-Plana formula&lt;/a&gt;, which I've also recently implemented as &lt;tt&gt;sumap()&lt;/tt&gt;. The Abel-Plana formula gives the exact value for an infinite series (subject to some special growth conditions on the analytically extended summand) as a sum of two integrals.&lt;br /&gt;&lt;br /&gt;The Abel-Plana formula is particularly useful when the summand decreases like a power of &lt;i&gt;k&lt;/i&gt;; for example when the sum is a pure zeta function:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;&gt;&gt;&gt; sumap(lambda k: 1/k**2.5, [1,inf])&lt;br /&gt;1.34148725725091717975677&lt;br /&gt;&gt;&gt;&gt; zeta(2.5)&lt;br /&gt;1.34148725725091717975677&lt;br /&gt;&lt;br /&gt;&gt;&gt;&gt; sumap(lambda k: 1/(k+1j)**(2.5+2.5j), [1,inf])&lt;br /&gt;(-3.385361068546473342286084 - 0.7432082105196321803869551j)&lt;br /&gt;&gt;&gt;&gt; zeta(2.5+2.5j, 1+1j)&lt;br /&gt;(-3.385361068546473342286084 - 0.7432082105196321803869551j)&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;It actually works very well for the generalized hypergeometric series at the &lt;i&gt;z&lt;/i&gt; = 1 singularity (which, as mentioned earlier, is a generalized polygamma function, polylogarithm, or zeta function of integer argument), and near the singularity as well. It should even be slightly faster than the Euler-Maclaurin formula since no derivatives are required. Yet another possibility is to integrate a Mellin-Barnes type contour for the hypergeometric function directly.&lt;br /&gt;&lt;br /&gt;But at present, I don't want to modify the existing hypergeometric code because it works and would only get more complicated. Rather, I want to improve &lt;tt&gt;nsum&lt;/tt&gt; so it can handle all of this more easily without external hacks. The numerical integration code should also be improved first, because there are still certain parameter combinations where the hypergeometric function evaluation fails due to slow convergence in the numerical integration (this is due to an implementation issue and not an inherent limitation of the integration algorithm).&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4754734402679928849-8421138063566161330?l=fredrik-j.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://fredrik-j.blogspot.com/feeds/8421138063566161330/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=4754734402679928849&amp;postID=8421138063566161330' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4754734402679928849/posts/default/8421138063566161330'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4754734402679928849/posts/default/8421138063566161330'/><link rel='alternate' type='text/html' href='http://fredrik-j.blogspot.com/2010/07/euler-maclaurin-summation-of.html' title='Euler-Maclaurin summation of hypergeometric series'/><author><name>Fredrik Johansson</name><uri>http://www.blogger.com/profile/01465400860530971858</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://4.bp.blogspot.com/_rh0QblLk0C0/TFCIHTI0lEI/AAAAAAAAAUQ/j-LsprfzhkE/s72-c/h3.png' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4754734402679928849.post-4782331091283891322</id><published>2010-07-27T18:32:00.006+02:00</published><updated>2010-07-27T21:19:06.704+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='sage'/><category scheme='http://www.blogger.com/atom/ns#' term='sympy'/><category scheme='http://www.blogger.com/atom/ns#' term='mpmath'/><title type='text'>Post Sage Days 24 report</title><content type='html'>Now that I've overcome the immediate effects of SDWS (Sage Days Withdrawal Syndrome), I should write a brief report about &lt;a href="http://wiki.sagemath.org/days24"&gt;Sage Days 24&lt;/a&gt; which took place last week at the &lt;a href="http://www.risc.jku.at/"&gt;Research Institute for Symbolic Computation&lt;/a&gt; (RISC), located in a small town called Hagenberg just outside Linz in Austria. Many thanks for the organizers for inviting me!&lt;br /&gt;&lt;br /&gt;Since I'm starting as a Ph.D. student at RISC in October, it was nice to get an early view of the site and meet some current students and staff. The location is quite beautiful, my only complaint being the extremely high temperature last week (they say the weather is more normal most of the year!).&lt;br /&gt;&lt;br /&gt;SD24 was titled "Symbolic Computation in Differential Algebra and Special Functions", which pretty accurately describes the topics covered. Some points of interest:&lt;br /&gt;&lt;br /&gt;&lt;a href="http://modular.math.washington.edu/"&gt;William Stein&lt;/a&gt; talked about his vision for Sage (short and long term), including goals for special functions support.&lt;br /&gt;&lt;br /&gt;&lt;a href="http://algo.inria.fr/chyzak/"&gt;Frédéric Chyzak&lt;/a&gt; gave a talk about the &lt;a href="http://ddmf.msr-inria.inria.fr/ddmf"&gt;Dynamic Dictionary of Mathematical Functions&lt;/a&gt;. I really like the approach of starting from a minimal definition of a special function (a differential equation with initial conditions) and generating series expansions, Chebyshev approximations (etc.) algorithmically.&lt;br /&gt;&lt;br /&gt;&lt;a href="http://homepages.cwi.nl/~nicot/"&gt;Nico Temme&lt;/a&gt; talked about numerical evaluation of special functions, which was familiar grounds to me, but with much food for thought.&lt;br /&gt;&lt;br /&gt;I met &lt;a href="http://www.risc.jku.at/home/mkauers"&gt;Manuel Kauers&lt;/a&gt;, my future supervisor, who gave a neat &lt;a href="http://www.risc.uni-linz.ac.at/people/mkauers/publications/kauers10d.pdf"&gt;presentation&lt;/a&gt; of some recent research.&lt;br /&gt;&lt;br /&gt;There were many other interesting &lt;a href="http://wiki.sagemath.org/days24/schedule"&gt;talks&lt;/a&gt; and discussions as well, and I gave a talk about special function evaluation in mpmath (based on my SD23 talk). And of course, I met various other Sage developers and got some coding done as well.&lt;br /&gt;&lt;br /&gt;I had hoped to get generalized hypergeometric functions into Sage during SD24. There is now a &lt;a href="http://trac.sagemath.org/sage_trac/ticket/2516"&gt;patch&lt;/a&gt;, but it still needs some more work.&lt;br /&gt;&lt;br /&gt;I also implemented &lt;a href="http://dlmf.nist.gov/12"&gt;parabolic cylinder functions&lt;/a&gt; (PCFs) in mpmath, the last remaining chapter of hypergeometric functions listed in the &lt;a href="http://dlmf.nist.gov/"&gt;DLMF&lt;/a&gt; (mpmath now covers chapters 1-20, 22, 24-25, partially 26-27, and 33). &lt;a href="http://code.google.com/p/mpmath/source/detail?r=1178"&gt;Code commit here.&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Technically, parabolic cylinder functions are just scaled (linear combinations of) confluent hypergeometric functions or Whittaker functions, but they are a bit tricky to compute due to cancellation and branch cuts.&lt;br /&gt;&lt;br /&gt;An example from Nico Temme's talk was to evaluate &lt;i&gt;D&lt;/i&gt;&lt;sub&gt;&amp;nu;&lt;/sub&gt;(&lt;i&gt;z&lt;/i&gt;) with &amp;nu; = -300.14 and &lt;i&gt;z&lt;/i&gt; = 300.15 (Maple 13 takes 5 seconds to give an answer that has the wrong sign and is off by 692 orders of magnitude). The new &lt;tt&gt;pcfd&lt;/tt&gt; function in mpmath instantaneously (in about a millisecond) gives the correct result:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;&gt;&gt;&gt; pcfd(-300.14, 300.15)&lt;br /&gt;6.83322814925713e-10526&lt;br /&gt;&gt;&gt;&gt; mp.dps = 30&lt;br /&gt;&gt;&gt;&gt; pcfd('-300.14', '300.15')&lt;br /&gt;6.83322814923312844480669009487e-10526&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;All the curve plots from &lt;a href="http://dlmf.nist.gov/12.3"&gt;DLMF 12.3&lt;/a&gt; can be reproduced as follows:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;f1 = lambda x: pcfu(0.5,x)&lt;br /&gt;f2 = lambda x: pcfu(2,x)&lt;br /&gt;f3 = lambda x: pcfu(3.5,x)&lt;br /&gt;f4 = lambda x: pcfu(5,x)&lt;br /&gt;f5 = lambda x: pcfu(8,x)&lt;br /&gt;plot([f1,f2,f3,f4,f5], [-3,3], [0,3])&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://1.bp.blogspot.com/_rh0QblLk0C0/TE8rBfwSyWI/AAAAAAAAAT4/yXcCJbIwnBY/s1600/12.3.1.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 320px; height: 241px;" src="http://1.bp.blogspot.com/_rh0QblLk0C0/TE8rBfwSyWI/AAAAAAAAAT4/yXcCJbIwnBY/s320/12.3.1.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5498660974746585442" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;f1 = lambda x: pcfv(0.5,x)&lt;br /&gt;f2 = lambda x: pcfv(2,x)&lt;br /&gt;f3 = lambda x: pcfv(3.5,x)&lt;br /&gt;f4 = lambda x: pcfv(5,x)&lt;br /&gt;f5 = lambda x: pcfv(8,x)&lt;br /&gt;plot([f1,f2,f3,f4,f5], [-3,3], [-3,3])&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_rh0QblLk0C0/TE8q-HO2hkI/AAAAAAAAATw/WXCAP26FD4k/s1600/12.3.2.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 320px; height: 241px;" src="http://4.bp.blogspot.com/_rh0QblLk0C0/TE8q-HO2hkI/AAAAAAAAATw/WXCAP26FD4k/s320/12.3.2.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5498660916624262722" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;f1 = lambda x: pcfu(-0.5,x)&lt;br /&gt;f2 = lambda x: pcfu(-2,x)&lt;br /&gt;f3 = lambda x: pcfu(-3.5,x)&lt;br /&gt;f4 = lambda x: pcfu(-5,x)&lt;br /&gt;plot([f1,f2,f3,f4], [-8,8], [-6,6])&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://2.bp.blogspot.com/_rh0QblLk0C0/TE8q9LYwyzI/AAAAAAAAATo/0vcabZzyZAY/s1600/12.3.3.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 320px; height: 241px;" src="http://2.bp.blogspot.com/_rh0QblLk0C0/TE8q9LYwyzI/AAAAAAAAATo/0vcabZzyZAY/s320/12.3.3.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5498660900559702834" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;f1 = lambda x: pcfv(-0.5,x)&lt;br /&gt;f2 = lambda x: pcfv(-2,x)&lt;br /&gt;f3 = lambda x: pcfv(-3.5,x)&lt;br /&gt;f4 = lambda x: pcfv(-5,x)&lt;br /&gt;plot([f1,f2,f3,f4], [-8,8], [-6,6])&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://2.bp.blogspot.com/_rh0QblLk0C0/TE8q8_dxhVI/AAAAAAAAATg/yDsmRuu6U08/s1600/12.3.4.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 320px; height: 241px;" src="http://2.bp.blogspot.com/_rh0QblLk0C0/TE8q8_dxhVI/AAAAAAAAATg/yDsmRuu6U08/s320/12.3.4.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5498660897359496530" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;f1 = lambda x: pcfu(-8,x)&lt;br /&gt;f2 = lambda x: pcfv(-8,x)*gamma(0.5-(-8))&lt;br /&gt;f3 = lambda x: hypot(f1(x), f2(x))&lt;br /&gt;plot([f1,f2,f3], [-6,6], [-120,120])&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://1.bp.blogspot.com/_rh0QblLk0C0/TE8q8md5fTI/AAAAAAAAATY/a3iHULych3w/s1600/12.3.5.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 320px; height: 241px;" src="http://1.bp.blogspot.com/_rh0QblLk0C0/TE8q8md5fTI/AAAAAAAAATY/a3iHULych3w/s320/12.3.5.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5498660890649132338" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;f1 = lambda x: diff(pcfu,(-8,x),(0,1))&lt;br /&gt;f2 = lambda x: diff(pcfv,(-8,x),(0,1))*gamma(0.5-(-8))&lt;br /&gt;f3 = lambda x: hypot(f1(x), f2(x))&lt;br /&gt;plot([f1,f2,f3], [-6,6], [-220,220])&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://1.bp.blogspot.com/_rh0QblLk0C0/TE8q8fCSxkI/AAAAAAAAATQ/S8ziTJv4nf4/s1600/12.3.6.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 320px; height: 241px;" src="http://1.bp.blogspot.com/_rh0QblLk0C0/TE8q8fCSxkI/AAAAAAAAATQ/S8ziTJv4nf4/s320/12.3.6.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5498660888654300738" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;A little more information can be found in the &lt;a href="http://mpmath.googlecode.com/svn/trunk/doc/build/functions/bessel.html#parabolic-cylinder-functions"&gt;mpmath documentation section for PCFs&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;Thanks again to the &lt;a href="http://www.nsf.gov/awardsearch/showAward.do?AwardNumber=0757627&amp;version=noscript"&gt;NSF grant&lt;/a&gt; enabling this work.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4754734402679928849-4782331091283891322?l=fredrik-j.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://fredrik-j.blogspot.com/feeds/4782331091283891322/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=4754734402679928849&amp;postID=4782331091283891322' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4754734402679928849/posts/default/4782331091283891322'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4754734402679928849/posts/default/4782331091283891322'/><link rel='alternate' type='text/html' href='http://fredrik-j.blogspot.com/2010/07/post-sage-days-24-report.html' title='Post Sage Days 24 report'/><author><name>Fredrik Johansson</name><uri>http://www.blogger.com/profile/01465400860530971858</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://1.bp.blogspot.com/_rh0QblLk0C0/TE8rBfwSyWI/AAAAAAAAAT4/yXcCJbIwnBY/s72-c/12.3.1.png' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4754734402679928849.post-3640420331571705232</id><published>2010-07-10T23:18:00.010+02:00</published><updated>2010-07-11T01:05:37.476+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='sage'/><category scheme='http://www.blogger.com/atom/ns#' term='sympy'/><category scheme='http://www.blogger.com/atom/ns#' term='mpmath'/><title type='text'>Sage Days 23, and Bessel function zeros</title><content type='html'>Yesterday I came back home from &lt;a href="http://wiki.sagemath.org/days23"&gt;Sage Days 23&lt;/a&gt; in Leiden. I don't really have time to write a detailed travel report right now, but overall it was very, very nice. Those interested can check out William Stein's photos (&lt;a href="http://picasaweb.google.com/wstein/20100705Sagedays23Day1"&gt;1&lt;/a&gt;, &lt;a href="http://picasaweb.google.com/wstein/20100706Sagedays23Day2"&gt;2&lt;/a&gt;, &lt;a href="http://picasaweb.google.com/wstein/20100707Sagedays23Day3"&gt;3&lt;/a&gt;, &lt;a href="http://picasaweb.google.com/wstein/20100709Sagedays23Day4"&gt;4&lt;/a&gt;) of the event (I can be seen in a few of the photos, wearing a blue t-shirt during days 1-3 and a yellow t-shirt during day 4).&lt;br /&gt;&lt;br /&gt;I didn't get quite as much coding done as I probably had time for (in part because I had a talk to prepare, and in part because I spent a bunch of time playing with &lt;a href="http://www.mpir.org/"&gt;MPIR&lt;/a&gt; and &lt;a href="http://www.flintlib.org/"&gt;FLINT 2&lt;/a&gt; and gaining some insights but otherwise not accomplishing much productive).&lt;br /&gt;&lt;br /&gt;The code I did write includes a complete Cython implementation of real exp (giving up to a 7x speedup) and complex sqrt for mpmath, as well as code for fast evaluation of the gamma function at rational arguments. I will submit the Cython code to Sage soon; possibly after rewriting the remaining power code in Cython as well.&lt;br /&gt;&lt;br /&gt;Anyway, prompted by a &lt;a href="http://groups.google.com/group/sage-support/browse_frm/thread/3761a256e868dbe1/7eb7e755585140f1?hl=en"&gt;recent question on sage-support&lt;/a&gt; (and because I finally got inspiration to do it while at the airport), I've now implemented &lt;a href="http://code.google.com/p/mpmath/source/detail?r=1177"&gt;Bessel function zeros&lt;/a&gt; in mpmath.&lt;br /&gt;&lt;br /&gt;Bessel function zeros are very important in applied mathematics because they are needed for &lt;a href="http://en.wikipedia.org/wiki/Fourier%E2%80%93Bessel_series"&gt;Fourier–Bessel series&lt;/a&gt;, used to solve partial differential equations in cylindrical coordinates. I'd like to write down a concrete computational example, but it'd be slightly involved and I don't really have time right now. Perhaps in a later blog post.&lt;br /&gt;&lt;br /&gt;The code permits computing the &lt;i&gt;m&lt;/i&gt;-th nonnegative simple zero of &lt;i&gt;J&lt;sub&gt;&amp;nu;&lt;/sub&gt;&lt;/i&gt;(&lt;i&gt;z&lt;/i&gt;), &lt;i&gt;J'&lt;sub&gt;&amp;nu;&lt;/sub&gt;&lt;/i&gt;(&lt;i&gt;z&lt;/i&gt;) &lt;i&gt;Y&lt;sub&gt;&amp;nu;&lt;/sub&gt;&lt;/i&gt;(&lt;i&gt;z&lt;/i&gt;), or &lt;i&gt;Y'&lt;sub&gt;&amp;nu;&lt;/sub&gt;&lt;/i&gt;(&lt;i&gt;z&lt;/i&gt;) for any positive integer index &lt;i&gt;m&lt;/i&gt; and real order &amp;nu; &amp;ge; 0. One can use it for large &lt;i&gt;m&lt;/i&gt;, large &amp;nu;, or both:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;&gt;&gt;&gt; besseljzero(1,100)&lt;br /&gt;314.9434728377671624580656&lt;br /&gt;&gt;&gt;&gt; besseljzero(100,1)&lt;br /&gt;108.836165898409774363098&lt;br /&gt;&gt;&gt;&gt; besseljzero(100,100)&lt;br /&gt;459.5295465754674695983379&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;A word of caution: I've chosen the convention Abramowitz &amp; Stegun of including &lt;i&gt;z&lt;/i&gt; = 0 for &lt;i&gt;J'&lt;/i&gt;&lt;sub&gt;0&lt;/sub&gt;, which is incompatible with SciPy and the Mathematica BesselZeros package. The A&amp;S convention makes much more sense to me because it makes the zeros a continuous function of the order. This case (&amp;nu;=0, &lt;i&gt;derivative=1&lt;/i&gt;) should be the only incompatibility.&lt;br /&gt;&lt;br /&gt;It's a bit tricky to compute the zeros of Bessel functions, because (as far as I know) no globally valid approximations are known; not any reasonably simple ones anyway.&lt;br /&gt;&lt;br /&gt;Asymptotic expansions are known with respect to either the order or the index. In my implementation, I've chosen to simply use the asymptotic expansion with respect to the index &lt;i&gt;n&lt;/i&gt;. If the &lt;i&gt;n&lt;/i&gt; is too small, the code simply chooses a larger index &lt;i&gt;m&lt;/i&gt; for which the asymptotic expansion does work, and then isolates &lt;i&gt;all&lt;/i&gt; the zeros between 0 and the &lt;i&gt;m&lt;/i&gt;-th by counting sign changes. By caching this result, all the consecutive zeros for the same function order can then be computed quickly as well.&lt;br /&gt;&lt;br /&gt;This approach is simple and quite robust (up to any silly implementation errors on my part), although the code could certainly be optimized more for large orders.&lt;br /&gt;&lt;br /&gt;The following graphic shows the derivative of &lt;i&gt;Y&lt;/i&gt;&lt;sub&gt;50&lt;/sub&gt;(&lt;i&gt;z&lt;/i&gt;) with its zeros marked:&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://2.bp.blogspot.com/_rh0QblLk0C0/TDjyilf5NfI/AAAAAAAAAR8/Ch9gNp6c0Hs/s1600/yzeros.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 400px; height: 271px;" src="http://2.bp.blogspot.com/_rh0QblLk0C0/TDjyilf5NfI/AAAAAAAAAR8/Ch9gNp6c0Hs/s400/yzeros.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5492406421573678578" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;A neat property of Bessel functions is that they can be expanded as infinite products over their zeros:&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://1.bp.blogspot.com/_rh0QblLk0C0/TDj59oxEItI/AAAAAAAAASM/Mgm-4wxLQnc/s1600/E15.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 233px; height: 49px;" src="http://1.bp.blogspot.com/_rh0QblLk0C0/TDj59oxEItI/AAAAAAAAASM/Mgm-4wxLQnc/s400/E15.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5492414582888866514" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_rh0QblLk0C0/TDj59YOv2zI/AAAAAAAAASE/mwmutm-0X1M/s1600/E16.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 231px; height: 51px;" src="http://3.bp.blogspot.com/_rh0QblLk0C0/TDj59YOv2zI/AAAAAAAAASE/mwmutm-0X1M/s400/E16.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5492414578449963826" /&gt;&lt;/a&gt;&lt;br /&gt;These products converge slowly, but work with the aid of convergence acceleration:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;&gt;&gt;&gt; v, z = mpf(3), mpf(0.75)&lt;br /&gt;&gt;&gt;&gt; (z/2)**v/gamma(v+1)*&lt;br /&gt;...     nprod(lambda k: 1-(z/besseljzero(v,k))**2, [1,inf])&lt;br /&gt;...&lt;br /&gt;0.008484383423274108843927552&lt;br /&gt;&gt;&gt;&gt; besselj(v,z)&lt;br /&gt;0.008484383423274108843927552&lt;br /&gt;&lt;br /&gt;&gt;&gt;&gt; (z/2)**(v-1)/2/gamma(v)*&lt;br /&gt;...     nprod(lambda k: 1-(z/besseljzero(v,k,1))**2, [1,inf])&lt;br /&gt;...&lt;br /&gt;0.0331364636065541211306414&lt;br /&gt;&gt;&gt;&gt; besselj(v,z,1)&lt;br /&gt;0.0331364636065541211306414&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;I will do more work on Bessel functions in mpmath and Sage, so watch for future updates.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4754734402679928849-3640420331571705232?l=fredrik-j.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://fredrik-j.blogspot.com/feeds/3640420331571705232/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=4754734402679928849&amp;postID=3640420331571705232' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4754734402679928849/posts/default/3640420331571705232'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4754734402679928849/posts/default/3640420331571705232'/><link rel='alternate' type='text/html' href='http://fredrik-j.blogspot.com/2010/07/sage-days-23-and-bessel-function-zeros.html' title='Sage Days 23, and Bessel function zeros'/><author><name>Fredrik Johansson</name><uri>http://www.blogger.com/profile/01465400860530971858</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://2.bp.blogspot.com/_rh0QblLk0C0/TDjyilf5NfI/AAAAAAAAAR8/Ch9gNp6c0Hs/s72-c/yzeros.png' height='72' width='72'/><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4754734402679928849.post-6264164165855848670</id><published>2010-07-02T15:37:00.007+02:00</published><updated>2010-07-02T16:44:31.075+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='sage'/><category scheme='http://www.blogger.com/atom/ns#' term='sympy'/><category scheme='http://www.blogger.com/atom/ns#' term='mpmath'/><title type='text'>Symbolic infinite series</title><content type='html'>I've written a little module for symbolically evaluating hypergeometric infinite series. Technically, a series is hypergeometric if the ratio between successive terms is a rational function of the index &lt;i&gt;k&lt;/i&gt;; more intuitively, most "nice-looking" series (such as power series of most special functions) involving powers, factorials, gamma functions, binomial coefficients (etc.) are hypergeometric.&lt;br /&gt;&lt;br /&gt;The &lt;a href="http://fredrikj.net/code/hyperpy5.py"&gt;code&lt;/a&gt; currently uses SymPy, but should be possible to convert to Sage's symbolics (it mostly requires basic pattern matching, and evaluation of standard special functions). Perhaps "rewrite" is a better word than "convert" because the current code is a messy hack. It's buggy, and only handles special cases in places where a general approach could be used (such as for rational functions).&lt;br /&gt;&lt;br /&gt;The basic idea behind the code is to perform the evaluation in two steps. Firstly, the given series is converted into canonical form. I've used the standard generalized hypergeometric series &lt;i&gt;&lt;sub&gt;p&lt;/sub&gt;F&lt;sub&gt;q&lt;/sub&gt;&lt;/i&gt;. This is not always an optimal representation, but usually does the job.&lt;br /&gt;&lt;br /&gt;In the second step, parameter transformations and table lookups are used to reduce the &lt;i&gt;&lt;sub&gt;p&lt;/sub&gt;F&lt;sub&gt;q&lt;/sub&gt;&lt;/i&gt; series into elementary (or simpler) functions when possible. If this step fails, the &lt;i&gt;&lt;sub&gt;p&lt;/sub&gt;F&lt;sub&gt;q&lt;/sub&gt;&lt;/i&gt; function is returned, giving a closed-form solution that can be manipulated or evaluated numerically.&lt;br /&gt;&lt;br /&gt;The second step is the most difficult, because the transformation algorithms that need to be used for hypergeometric functions are very complicated in general (and there are all kinds of special cases). So far I've only implemented some simple transformations. For the final lookup stage, it would be straightforward to also implement expansion into Bessel functions, incomplete gamma functions, etc.&lt;br /&gt;&lt;br /&gt;Below is a sample of some working test cases, with numerical evaluation of both the evaluated result and the original series as verification. Some of the sums don't quite simplify fully, i.e. there is an unevaluated &lt;i&gt;&lt;sub&gt;p&lt;/sub&gt;F&lt;sub&gt;q&lt;/sub&gt;&lt;/i&gt; function left. The formulas were mostly generated with SymPy's &lt;tt&gt;latex&lt;/tt&gt; function, so they are not always as pretty as could be.&lt;br /&gt;&lt;br /&gt;&lt;p&gt;&lt;img style="display: inline;" alt="[;   \sum_{k=0}^{\infty} k!^{-1} = e ;]" title=" \sum_{k=0}^{\infty} k!^{-1} =   e " src="http://fredrikj.net/blah/gif_034.gif"&gt;&lt;/p&gt;&lt;pre&gt;&amp;gt;&amp;gt;&amp;gt; hypsum(1 / fac(k), k)&lt;br /&gt;E&lt;br /&gt;&amp;gt;&amp;gt;&amp;gt; _.evalf()&lt;br /&gt;2.71828182845905&lt;br /&gt;&amp;gt;&amp;gt;&amp;gt; mpmath.nsum(lambda k: 1 / mpmath.fac(k), [0,mpmath.inf])&lt;br /&gt;2.71828182845905&lt;br /&gt;&lt;/pre&gt;&lt;p&gt;&lt;img style="display: inline;" alt="[; \sum_{k=0}^{\infty}   \frac{k^{6}}{k!} = 203 e ;]" title=" \sum_{k=0}^{\infty}   \frac{k^{6}}{k!} = 203 e " src="http://fredrikj.net/blah/gif_031.gif"&gt;&lt;/p&gt;&lt;pre&gt;&amp;gt;&amp;gt;&amp;gt; hypsum(k**6 / fac(k), k)&lt;br /&gt;203*E&lt;br /&gt;&amp;gt;&amp;gt;&amp;gt; _.evalf()&lt;br /&gt;551.811211177186&lt;br /&gt;&amp;gt;&amp;gt;&amp;gt; mpmath.nsum(lambda k: k**6 / mpmath.fac(k), [0,mpmath.inf])&lt;br /&gt;551.811211177186&lt;br /&gt;&lt;/pre&gt;&lt;p&gt;&lt;img style="display: inline;" alt="[; \sum_{k=0}^{\infty} 4   \frac{d k z^{2 + k}}{k!} = 4 d z^{3} e^{z} ;]" title="   \sum_{k=0}^{\infty} 4 \frac{d k z^{2 + k}}{k!} = 4 d z^{3} e^{z} "   src="http://fredrikj.net/blah/gif_012.gif"&gt;&lt;/p&gt;&lt;pre&gt;&amp;gt;&amp;gt;&amp;gt; hypsum(4 * d * k * z**(k+2) / fac(k), k)&lt;br /&gt;4*d*z**3*exp(z)&lt;br /&gt;&amp;gt;&amp;gt;&amp;gt; _.evalf(subs={d:'1/2',z:'1/4'})&lt;br /&gt;0.0401257942714919&lt;br /&gt;&amp;gt;&amp;gt;&amp;gt; mpmath.nsum(lambda k: 4 * 0.5 * k * 0.25**(k+2) / mpmath.fac(k), [0,mpmath.inf])&lt;br /&gt;0.0401257942714919&lt;br /&gt;&lt;/pre&gt;&lt;p&gt;&lt;img style="display: inline;" alt="[; \sum_{k=0}^{\infty}   \left({\left(2\right)}_{k}\right)^{-1} = -1 + e ;]" title="   \sum_{k=0}^{\infty} \left({\left(2\right)}_{k}\right)^{-1} = -1 + e "   src="http://fredrikj.net/blah/gif_010.gif"&gt;&lt;/p&gt;&lt;pre&gt;&amp;gt;&amp;gt;&amp;gt; hypsum(1 / rf(2,k), k)&lt;br /&gt;-1 + E&lt;br /&gt;&amp;gt;&amp;gt;&amp;gt; _.evalf()&lt;br /&gt;1.71828182845905&lt;br /&gt;&amp;gt;&amp;gt;&amp;gt; mpmath.nsum(lambda k: 1 / mpmath.rf(2,k), [0,mpmath.inf])&lt;br /&gt;1.71828182845905&lt;br /&gt;&lt;/pre&gt;&lt;p&gt;&lt;img style="display: inline;" alt=""   title=" \sum_{k=0}^{\infty} \frac{k^{3} z^{k}}{k!} = z e^{z} + z^{3}   e^{z} + 3 z^{2} e^{z} " src="http://fredrikj.net/blah/gif_039.gif"&gt;&lt;/p&gt;&lt;pre&gt;&amp;gt;&amp;gt;&amp;gt; simplify(hypsum(k**3 * z**(k) / fac(k), k))&lt;br /&gt;z*exp(z) + z**3*exp(z) + 3*z**2*exp(z)&lt;br /&gt;&amp;gt;&amp;gt;&amp;gt; _.evalf(subs={d:'1/2',z:'1/4'})&lt;br /&gt;0.581824016936633&lt;br /&gt;&amp;gt;&amp;gt;&amp;gt; mpmath.nsum(lambda k: k**3 * 0.25**(k) / mpmath.fac(k), [0,mpmath.inf])&lt;br /&gt;0.581824016936633&lt;br /&gt;&lt;/pre&gt;&lt;p&gt;&lt;img style="display: inline;" alt="[; \sum_{k=0}^{\infty} {{2   k}\choose{k}}^{-1} = \frac{4}{3} + \frac{2}{27} \pi \sqrt{3} ;]" title="   \sum_{k=0}^{\infty} {{2 k}\choose{k}}^{-1} = \frac{4}{3} + \frac{2}{27}   \pi \sqrt{3} " src="http://fredrikj.net/blah/gif_035.gif"&gt;&lt;/p&gt;&lt;pre&gt;&amp;gt;&amp;gt;&amp;gt; simplify(hypsum(1 / binomial(2*k,k), k))&lt;br /&gt;4/3 + 2*pi*3**(1/2)/27  &amp;gt;&amp;gt;&amp;gt; _.evalf()&lt;br /&gt;1.73639985871872&lt;br /&gt;&amp;gt;&amp;gt;&amp;gt; mpmath.nsum(lambda k: 1 / mpmath.binomial(2*k,k), [0,mpmath.inf])&lt;br /&gt;1.73639985871872&lt;br /&gt;&lt;/pre&gt;&lt;p&gt;&lt;img style="display: inline;" alt="[; \sum_{k=0}^{\infty} {{3   k}\choose{k}}^{-1} = \frac{2}{3} \frac{\pi \sqrt{3}   \,_{3}F_{2}\left(\frac{1}{2},1,1; \frac{1}{3},\frac{2}{3};   \frac{4}{27}\right)}{\operatorname{\Gamma}\left(\frac{1}{3}\right)   \operatorname{\Gamma}\left(\frac{2}{3}\right)} ;]" title="   \sum_{k=0}^{\infty} {{3 k}\choose{k}}^{-1} = \frac{2}{3} \frac{\pi   \sqrt{3} \,_{3}F_{2}\left(\frac{1}{2},1,1; \frac{1}{3},\frac{2}{3};   \frac{4}{27}\right)}{\operatorname{\Gamma}\left(\frac{1}{3}\right)   \operatorname{\Gamma}\left(\frac{2}{3}\right)} "   src="http://fredrikj.net/blah/gif_019.gif"&gt;&lt;/p&gt;&lt;pre&gt;&amp;gt;&amp;gt;&amp;gt; hypsum(1 / binomial(3*k,k), k)&lt;br /&gt;2*pi*3**(1/2)*3F2([1/2, 1, 1], [1/3, 2/3], 4/27)/(3*gamma(1/3)*gamma(2/3))&lt;br /&gt;&amp;gt;&amp;gt;&amp;gt; _.evalf()&lt;br /&gt;1.41432204432182&lt;br /&gt;&amp;gt;&amp;gt;&amp;gt; mpmath.nsum(lambda k: 1 / mpmath.binomial(3*k,k), [0,mpmath.inf])&lt;br /&gt;1.41432204432182&lt;br /&gt;&lt;/pre&gt;&lt;p&gt;&lt;img style="display: inline;" alt="[; \sum_{k=0}^{\infty}   \frac{k^{4}}{{{2 k}\choose{k}}} = \frac{32}{3} + \frac{238}{243} \pi   \sqrt{3} ;]" title=" \sum_{k=0}^{\infty} \frac{k^{4}}{{{2 k}\choose{k}}}   = \frac{32}{3} + \frac{238}{243} \pi \sqrt{3} "   src="http://fredrikj.net/blah/gif_002.gif"&gt;&lt;/p&gt;&lt;pre&gt;&amp;gt;&amp;gt;&amp;gt; hypsum(k**4 / binomial(2*k,k), k)&lt;br /&gt;32/3 + 238*pi*3**(1/2)/243&lt;br /&gt;&amp;gt;&amp;gt;&amp;gt; _.evalf()&lt;br /&gt;15.9961018356512&lt;br /&gt;&amp;gt;&amp;gt;&amp;gt; mpmath.nsum(lambda k: k**4 / mpmath.binomial(2*k,k), [0,mpmath.inf])&lt;br /&gt;15.9961018356512&lt;br /&gt;&lt;/pre&gt;&lt;p&gt;&lt;img style="display: inline;" alt="[; \sum_{k=0}^{\infty}   \frac{\left(1 + k\right)! \left(2 + k\right)!}{\left(3 + k\right)!   \left(4 + k\right)!} = \frac{1}{72} \,_{3}F_{2}\left(1,2,3; 4,5;   1\right) ;]" title=" \sum_{k=0}^{\infty} \frac{\left(1 + k\right)!   \left(2 + k\right)!}{\left(3 + k\right)! \left(4 + k\right)!} =   \frac{1}{72} \,_{3}F_{2}\left(1,2,3; 4,5; 1\right) "   src="http://fredrikj.net/blah/gif_005.gif"&gt;&lt;/p&gt;&lt;pre&gt;&amp;gt;&amp;gt;&amp;gt; hypsum(fac(k+1)*fac(k+2)/fac(k+3)/fac(k+4), k)&lt;br /&gt;3F2([1, 2, 3], [4, 5], 1)/72&lt;br /&gt;&amp;gt;&amp;gt;&amp;gt; _.evalf()&lt;br /&gt;0.0217325998184402&lt;br /&gt;&amp;gt;&amp;gt;&amp;gt; mpmath.nsum(lambda k: mpmath.fac(k+1)*mpmath.fac(k+2)/mpmath.fac(k+3)/mpmath.fac(k+4), [0,mpmath.inf])&lt;br /&gt;0.0217325998184402&lt;br /&gt;&lt;/pre&gt;&lt;p&gt;&lt;img style="display: inline;" alt="[; \sum_{k=0}^{\infty}   \frac{1}{\left(3 + k\right)^{2} \left(8 + 6 k + k^{2}\right)} =   \frac{1}{72} \,_{3}F_{2}\left(1,2,3; 4,5; 1\right) ;]" title="   \sum_{k=0}^{\infty} \frac{1}{\left(3 + k\right)^{2} \left(8 + 6 k +   k^{2}\right)} = \frac{1}{72} \,_{3}F_{2}\left(1,2,3; 4,5; 1\right) "   src="http://fredrikj.net/blah/gif_028.gif"&gt;&lt;/p&gt;&lt;pre&gt;&amp;gt;&amp;gt;&amp;gt; hypsum(1/((3+k)**2*(8+6*k+k**2)), k)&lt;br /&gt;3F2([1, 2, 3], [4, 5], 1)/72&lt;br /&gt;&amp;gt;&amp;gt;&amp;gt; _.evalf()&lt;br /&gt;0.0217325998184402&lt;br /&gt;&amp;gt;&amp;gt;&amp;gt; mpmath.nsum(lambda k: 1/((3+k)**2*(8+6*k+k**2)), [0,mpmath.inf])&lt;br /&gt;0.0217325998184402&lt;br /&gt;&lt;/pre&gt;&lt;p&gt;&lt;img style="display: inline;" alt="[; \sum_{k=0}^{\infty}   \frac{\left(1 + 2 k\right)!}{\left(1 + k\right) \left(2 + 2 k\right)!} =   \frac{1}{12} \pi^{2} ;]" title=" \sum_{k=0}^{\infty} \frac{\left(1 + 2   k\right)!}{\left(1 + k\right) \left(2 + 2 k\right)!} = \frac{1}{12}   \pi^{2} " src="http://fredrikj.net/blah/gif_025.gif"&gt;&lt;/p&gt;&lt;pre&gt;&amp;gt;&amp;gt;&amp;gt; hypsum(fac(2*k+1)/(fac(2*k+2)*(k+1)), k)&lt;br /&gt;pi**2/12&lt;br /&gt;&amp;gt;&amp;gt;&amp;gt; _.evalf()&lt;br /&gt;0.822467033424113&lt;br /&gt;&amp;gt;&amp;gt;&amp;gt; mpmath.nsum(lambda k: mpmath.fac(2*k+1)/(mpmath.fac(2*k+2)*(k+1)), [0,mpmath.inf])&lt;br /&gt;0.822467033424113&lt;br /&gt;&lt;/pre&gt;&lt;p&gt;&lt;img style="display: inline;" alt="[; \sum_{k=0}^{\infty}   \frac{\left(1 + 2 k\right)!}{\left(2 + 3 k\right)!} = \frac{2}{27}   \frac{\pi \sqrt{3} \,_{2}F_{2}\left(1,\frac{3}{2};   \frac{4}{3},\frac{5}{3};   \frac{4}{27}\right)}{\operatorname{\Gamma}\left(\frac{4}{3}\right)   \operatorname{\Gamma}\left(\frac{5}{3}\right)} ;]" title="   \sum_{k=0}^{\infty} \frac{\left(1 + 2 k\right)!}{\left(2 + 3 k\right)!} =   \frac{2}{27} \frac{\pi \sqrt{3} \,_{2}F_{2}\left(1,\frac{3}{2};   \frac{4}{3},\frac{5}{3};   \frac{4}{27}\right)}{\operatorname{\Gamma}\left(\frac{4}{3}\right)   \operatorname{\Gamma}\left(\frac{5}{3}\right)} "   src="http://fredrikj.net/blah/gif_023.gif"&gt;&lt;/p&gt;&lt;pre&gt;&amp;gt;&amp;gt;&amp;gt; hypsum(fac(2*k+1)/(fac(3*k+2)), k)&lt;br /&gt;2*pi*3**(1/2)*2F2([1, 3/2], [4/3, 5/3], 4/27)/(27*gamma(4/3)*gamma(5/3))&lt;br /&gt;&amp;gt;&amp;gt;&amp;gt; _.evalf()&lt;br /&gt;0.553106730441975&lt;br /&gt;&amp;gt;&amp;gt;&amp;gt; mpmath.nsum(lambda k: mpmath.fac(2*k+1)/(mpmath.fac(3*k+2)), [0,mpmath.inf])&lt;br /&gt;0.553106730441975&lt;br /&gt;&lt;/pre&gt;&lt;p&gt;&lt;img style="display: inline;" alt="[; \sum_{k=0}^{\infty}   \frac{4^{k} \left(\frac{3}{2} + k\right)! \left(\frac{5}{2} +   k\right)!}{\left(6 + 2 k\right)!} = \frac{3}{256} \pi ;]" title="   \sum_{k=0}^{\infty} \frac{4^{k} \left(\frac{3}{2} + k\right)!   \left(\frac{5}{2} + k\right)!}{\left(6 + 2 k\right)!} = \frac{3}{256}   \pi " src="http://fredrikj.net/blah/gif_013.gif"&gt;&lt;/p&gt;&lt;pre&gt;&amp;gt;&amp;gt;&amp;gt; hypsum(4**k*fac(k+R32)*fac(k+R52)/(fac(2*k+6)), k)&lt;br /&gt;3*pi/256&lt;br /&gt;&amp;gt;&amp;gt;&amp;gt; _.evalf()&lt;br /&gt;0.0368155389092554&lt;br /&gt;&amp;gt;&amp;gt;&amp;gt; mpmath.nsum(lambda k: 4**k*mpmath.fac(k+1.5)*mpmath.fac(k+2.5)/(mpmath.fac(2*k+6)), [0,mpmath.inf], method='e')&lt;br /&gt;0.0368155389092358&lt;br /&gt;&lt;/pre&gt;&lt;p&gt;&lt;img style="display: inline;" alt="[; \sum_{k=0}^{\infty}   \frac{\left(-1\right)^{k} z^{1 + 2 k}}{\left(1 + 2 k\right) k!} =   \frac{1}{2} \sqrt{\pi} \operatorname{erf}\left(z\right) ;]" title="   \sum_{k=0}^{\infty} \frac{\left(-1\right)^{k} z^{1 + 2 k}}{\left(1 + 2   k\right) k!} = \frac{1}{2} \sqrt{\pi} \operatorname{erf}\left(z\right) "   src="http://fredrikj.net/blah/gif_027.gif"&gt;&lt;/p&gt;&lt;pre&gt;&amp;gt;&amp;gt;&amp;gt; hypsum((-1)**k * z**(2*k+1) / fac(k) / (2*k+1), k)&lt;br /&gt;pi**(1/2)*erf(z)/2&lt;br /&gt;&amp;gt;&amp;gt;&amp;gt; _.evalf(subs={d:'1/2',z:'1/4'})&lt;br /&gt;0.244887887180256&lt;br /&gt;&amp;gt;&amp;gt;&amp;gt; mpmath.nsum(lambda k: (-1)**k * 0.25**(2*k+1) / mpmath.fac(k) / (2*k+1), [0,mpmath.inf])&lt;br /&gt;0.244887887180256&lt;br /&gt;&lt;/pre&gt;&lt;p&gt;&lt;img style="display: inline;" alt="[; \sum_{k=0}^{\infty}   \frac{z^{1 + 2 k}}{1 + 2 k} = \operatorname{atanh}\left(z\right) ;]"   title=" \sum_{k=0}^{\infty} \frac{z^{1 + 2 k}}{1 + 2 k} =   \operatorname{atanh}\left(z\right) " src="http://fredrikj.net/blah/gif_011.gif"&gt;&lt;/p&gt;&lt;pre&gt;&amp;gt;&amp;gt;&amp;gt; hypsum(z**(2*k+1) / (2*k+1), k)&lt;br /&gt;atanh(z)&lt;br /&gt;&amp;gt;&amp;gt;&amp;gt; _.evalf(subs={d:'1/2',z:'1/4'})&lt;br /&gt;0.255412811882995&lt;br /&gt;&amp;gt;&amp;gt;&amp;gt; mpmath.nsum(lambda k: 0.25**(2*k+1) / (2*k+1), [0,mpmath.inf])&lt;br /&gt;0.255412811882995&lt;br /&gt;&lt;/pre&gt;&lt;p&gt;&lt;img style="display: inline;" alt="[; \sum_{k=0}^{\infty}   \frac{z^{k}}{1 + 2 k} =   \frac{\operatorname{atanh}\left(\sqrt{z}\right)}{\sqrt{z}} ;]" title="   \sum_{k=0}^{\infty} \frac{z^{k}}{1 + 2 k} =   \frac{\operatorname{atanh}\left(\sqrt{z}\right)}{\sqrt{z}} "   src="http://fredrikj.net/blah/gif_037.gif"&gt;&lt;/p&gt;&lt;pre&gt;&amp;gt;&amp;gt;&amp;gt; hypsum(z**k / (2*k+1), k)&lt;br /&gt;atanh(z**(1/2))/z**(1/2)&lt;br /&gt;&amp;gt;&amp;gt;&amp;gt; _.evalf(subs={d:'1/2',z:'1/4'})&lt;br /&gt;1.09861228866811&lt;br /&gt;&amp;gt;&amp;gt;&amp;gt; mpmath.nsum(lambda k: 0.25**k / (2*k+1), [0,mpmath.inf])&lt;br /&gt;1.09861228866811&lt;br /&gt;&lt;/pre&gt;&lt;p&gt;&lt;img style="display: inline;" alt="[; \sum_{k=0}^{\infty}   \frac{\left(- z\right)^{k}}{1 + k} = \frac{\operatorname{log}\left(1 +   z\right)}{z} ;]" title=" \sum_{k=0}^{\infty} \frac{\left(-   z\right)^{k}}{1 + k} = \frac{\operatorname{log}\left(1 + z\right)}{z} "   src="http://fredrikj.net/blah/gif_032.gif"&gt;&lt;/p&gt;&lt;pre&gt;&amp;gt;&amp;gt;&amp;gt; hypsum((-z)**k / (k+1), k)&lt;br /&gt;log(1 + z)/z&lt;br /&gt;&amp;gt;&amp;gt;&amp;gt; _.evalf(subs={d:'1/2',z:'1/4'})&lt;br /&gt;0.892574205256839&lt;br /&gt;&amp;gt;&amp;gt;&amp;gt; mpmath.nsum(lambda k: (-0.25)**k / (k+1), [0,mpmath.inf])&lt;br /&gt;0.892574205256839&lt;br /&gt;&lt;/pre&gt;&lt;p&gt;&lt;img style="display: inline;" alt="" title="   \sum_{k=0}^{\infty} \frac{z^{2 k} \left(- \frac{1}{2} +   k\right)!}{\left(1 + 2 k\right) k!} = \frac{\sqrt{\pi}   \operatorname{asin}\left(z\right)}{z} " src="http://fredrikj.net/blah/gif.gif"&gt;&lt;/p&gt;&lt;pre&gt;&amp;gt;&amp;gt;&amp;gt; hypsum(fac(k-R12)/((1+2*k)*fac(k))*z**(2*k), k)&lt;br /&gt;pi**(1/2)*asin(z)/z&lt;br /&gt;&amp;gt;&amp;gt;&amp;gt; _.evalf(subs={d:'1/2',z:'1/4'})&lt;br /&gt;1.79145636509746&lt;br /&gt;&amp;gt;&amp;gt;&amp;gt; mpmath.nsum(lambda k: mpmath.fac(k-0.5)/((1+2*k)*mpmath.fac(k))*0.25**(2*k), [0,mpmath.inf])&lt;br /&gt;1.79145636509746&lt;br /&gt;&lt;/pre&gt;&lt;p&gt;&lt;img style="display: inline;" alt="[; \sum_{k=0}^{\infty}   \frac{z^{k}}{\left(2 + k\right)!} = - \frac{1 + z - e^{z}}{z^{2}} ;]"   title=" \sum_{k=0}^{\infty} \frac{z^{k}}{\left(2 + k\right)!} = -   \frac{1 + z - e^{z}}{z^{2}} " src="http://fredrikj.net/blah/gif_018.gif"&gt;&lt;/p&gt;&lt;pre&gt;&amp;gt;&amp;gt;&amp;gt; hypsum(z**k / fac(k+2), k)&lt;br /&gt;-(1 + z - exp(z))/z**2&lt;br /&gt;&amp;gt;&amp;gt;&amp;gt; _.evalf(subs={d:'1/2',z:'1/4'})&lt;br /&gt;0.544406667003864&lt;br /&gt;&amp;gt;&amp;gt;&amp;gt; mpmath.nsum(lambda k: 0.25**k / mpmath.fac(k+2), [0,mpmath.inf])&lt;br /&gt;0.544406667003864&lt;br /&gt;&lt;/pre&gt;&lt;p&gt;&lt;img style="display: inline;" alt="[; \sum_{k=0}^{\infty}   \left(-5 + 3 z^{2}\right)^{3 - 4 k} = - \frac{\left(5 - 3   z^{2}\right)^{3}}{1 - \frac{1}{\left(5 - 3 z^{2}\right)^{4}}} ;]"   title=" \sum_{k=0}^{\infty} \left(-5 + 3 z^{2}\right)^{3 - 4 k} = -   \frac{\left(5 - 3 z^{2}\right)^{3}}{1 - \frac{1}{\left(5 - 3   z^{2}\right)^{4}}} " src="http://fredrikj.net/blah/gif_014.gif"&gt;&lt;/p&gt;&lt;pre&gt;&amp;gt;&amp;gt;&amp;gt; hypsum((3*z**2-5)**(-4*k+3), k)&lt;br /&gt;-(5 - 3*z**2)**3/(1 - 1/(5 - 3*z**2)**4)&lt;br /&gt;&amp;gt;&amp;gt;&amp;gt; _.evalf(subs={d:'1/2',z:'1/4'})&lt;br /&gt;-111.666432272586&lt;br /&gt;&amp;gt;&amp;gt;&amp;gt; mpmath.nsum(lambda k: (3*0.25**2-5)**(-4*k+3), [0,mpmath.inf])&lt;br /&gt;-111.666432272586&lt;br /&gt;&lt;/pre&gt;&lt;p&gt;&lt;img style="display: inline;" alt="[; \sum_{k=0}^{\infty}   \frac{z^{1 + 2 k}}{\left(1 + 2 k\right)!} =   \operatorname{sinh}\left(z\right) ;]" title=" \sum_{k=0}^{\infty}   \frac{z^{1 + 2 k}}{\left(1 + 2 k\right)!} =   \operatorname{sinh}\left(z\right) " src="http://fredrikj.net/blah/gif_016.gif"&gt;&lt;/p&gt;&lt;pre&gt;&amp;gt;&amp;gt;&amp;gt; hypsum(z**(2*k+1) / fac(2*k+1), k)&lt;br /&gt;sinh(z)&lt;br /&gt;&amp;gt;&amp;gt;&amp;gt; _.evalf(subs={d:'1/2',z:'1/4'})&lt;br /&gt;0.252612316808168&lt;br /&gt;&amp;gt;&amp;gt;&amp;gt; mpmath.nsum(lambda k: 0.25**(2*k+1) / mpmath.fac(2*k+1), [0,mpmath.inf])&lt;br /&gt;0.252612316808168&lt;br /&gt;&lt;/pre&gt;&lt;p&gt;&lt;img style="display: inline;" alt="[; \sum_{k=0}^{\infty}   \frac{\left(-1\right)^{k} z^{1 + 2 k}}{\left(1 + 2 k\right)!} =   \operatorname{sin}\left(z\right) ;]" title=" \sum_{k=0}^{\infty}   \frac{\left(-1\right)^{k} z^{1 + 2 k}}{\left(1 + 2 k\right)!} =   \operatorname{sin}\left(z\right) " src="http://fredrikj.net/blah/gif_022.gif"&gt;&lt;/p&gt;&lt;pre&gt;&amp;gt;&amp;gt;&amp;gt; hypsum((-1)**k * z**(2*k+1) / fac(2*k+1), k)&lt;br /&gt;sin(z)&lt;br /&gt;&amp;gt;&amp;gt;&amp;gt; _.evalf(subs={d:'1/2',z:'1/4'})&lt;br /&gt;0.247403959254523&lt;br /&gt;&amp;gt;&amp;gt;&amp;gt; mpmath.nsum(lambda k: (-1)**k * 0.25**(2*k+1) / mpmath.fac(2*k+1), [0,mpmath.inf])&lt;br /&gt;0.247403959254523&lt;br /&gt;&lt;/pre&gt;&lt;p&gt;&lt;img style="display: inline;" alt="[; \sum_{k=0}^{\infty}   \frac{\left(-1\right)^{k} d^{k} \left(1 - z\right)^{1 + 2 k}}{\left(2   k\right)!} = \left(1 - z\right) \operatorname{cos}\left(\sqrt{d} \left(1   - z\right)\right) ;]" title=" \sum_{k=0}^{\infty}   \frac{\left(-1\right)^{k} d^{k} \left(1 - z\right)^{1 + 2 k}}{\left(2   k\right)!} = \left(1 - z\right) \operatorname{cos}\left(\sqrt{d} \left(1   - z\right)\right) " src="http://fredrikj.net/blah/gif_038.gif"&gt;&lt;/p&gt;&lt;pre&gt;&amp;gt;&amp;gt;&amp;gt; hypsum((-1)**k * d**k * (1-z)**(2*k+1) / fac(2*k), k)&lt;br /&gt;(1 - z)*cos(d**(1/2)*(1 - z))&lt;br /&gt;&amp;gt;&amp;gt;&amp;gt; _.evalf(subs={d:'1/2',z:'1/4'})&lt;br /&gt;0.646980115568008&lt;br /&gt;&amp;gt;&amp;gt;&amp;gt; mpmath.nsum(lambda k: (-1)**k * 0.5**k * (1-0.25)**(2*k+1) / mpmath.fac(2*k), [0,mpmath.inf])&lt;br /&gt;0.646980115568008&lt;br /&gt;&lt;/pre&gt;&lt;p&gt;&lt;img style="display: inline;" alt="[; \sum_{k=0}^{\infty}   \frac{k z^{2 k}}{\left(1 + 2 k\right)!} = \frac{1}{2}   \operatorname{cosh}\left(z\right) -   \frac{\operatorname{sinh}\left(z\right)}{2 z} ;]" title="   \sum_{k=0}^{\infty} \frac{k z^{2 k}}{\left(1 + 2 k\right)!} =   \frac{1}{2} \operatorname{cosh}\left(z\right) -   \frac{\operatorname{sinh}\left(z\right)}{2 z} "   src="http://fredrikj.net/blah/gif_007.gif"&gt;&lt;/p&gt;&lt;pre&gt;&amp;gt;&amp;gt;&amp;gt; hypsum(k * z**(2*k) / fac(2*k+1), k)&lt;br /&gt;cosh(z)/2 - sinh(z)/(2*z)&lt;br /&gt;&amp;gt;&amp;gt;&amp;gt; _.evalf(subs={d:'1/2',z:'1/4'})&lt;br /&gt;0.0104819163234500&lt;br /&gt;&amp;gt;&amp;gt;&amp;gt; mpmath.nsum(lambda k: k * 0.25**(2*k) / mpmath.fac(2*k+1), [0,mpmath.inf])&lt;br /&gt;0.01048191632345&lt;br /&gt;&lt;/pre&gt;&lt;p&gt;&lt;img style="display: inline;" alt="[; \sum_{k=0}^{\infty}   \frac{\operatorname{\Gamma}^{2}\left(- \frac{1}{2} + k\right)}{k!^{2}} =   16 ;]" title=" \sum_{k=0}^{\infty}   \frac{\operatorname{\Gamma}^{2}\left(- \frac{1}{2} + k\right)}{k!^{2}} =   16 " src="http://fredrikj.net/blah/gif_020.gif"&gt;&lt;/p&gt;&lt;pre&gt;&amp;gt;&amp;gt;&amp;gt; hypsum(gamma(k-R12)**2/(fac(k)**2), k)&lt;br /&gt;16&lt;br /&gt;&amp;gt;&amp;gt;&amp;gt; _.evalf()&lt;br /&gt;16.0000000000000&lt;br /&gt;&amp;gt;&amp;gt;&amp;gt; mpmath.nsum(lambda k: mpmath.gamma(k-0.5)**2/(mpmath.fac(k)**2), [0,mpmath.inf])&lt;br /&gt;16.0&lt;br /&gt;&lt;/pre&gt;&lt;p&gt;&lt;img style="display: inline;" alt="[; \sum_{k=0}^{\infty}   \left(1 + k\right)^{-2} = \frac{1}{6} \pi^{2} ;]" title="   \sum_{k=0}^{\infty} \left(1 + k\right)^{-2} = \frac{1}{6} \pi^{2} "   src="http://fredrikj.net/blah/gif_029.gif"&gt;&lt;/p&gt;&lt;pre&gt;&amp;gt;&amp;gt;&amp;gt; hypsum(1/(k+1)**2, k)&lt;br /&gt;pi**2/6&lt;br /&gt;&amp;gt;&amp;gt;&amp;gt; _.evalf()&lt;br /&gt;1.64493406684823&lt;br /&gt;&amp;gt;&amp;gt;&amp;gt; mpmath.nsum(lambda k: 1/(k+1)**2, [0,mpmath.inf])&lt;br /&gt;1.64493406684823&lt;br /&gt;&lt;/pre&gt;&lt;p&gt;&lt;img style="display: inline;" alt="[; \sum_{k=0}^{\infty}   \frac{k}{\left(1 + k\right)^{3}} = - \operatorname{\zeta}\left(3\right) +   \frac{1}{6} \pi^{2} ;]" title=" \sum_{k=0}^{\infty} \frac{k}{\left(1 +   k\right)^{3}} = - \operatorname{\zeta}\left(3\right) + \frac{1}{6}   \pi^{2} " src="http://fredrikj.net/blah/gif_004.gif"&gt;&lt;/p&gt;&lt;pre&gt;&amp;gt;&amp;gt;&amp;gt; hypsum(k/(k+1)**3, k)&lt;br /&gt;-zeta(3) + pi**2/6&lt;br /&gt;&amp;gt;&amp;gt;&amp;gt; _.evalf()&lt;br /&gt;0.442877163688632&lt;br /&gt;&amp;gt;&amp;gt;&amp;gt; mpmath.nsum(lambda k: k/(k+1)**3, [0,mpmath.inf])&lt;br /&gt;0.442877163688632&lt;br /&gt;&lt;/pre&gt;&lt;p&gt;&lt;img style="display: inline;" alt="[; \sum_{k=0}^{\infty}   \frac{z^{k} \left(3 + k\right)}{\left(3 + 3 k\right) k!} = - \frac{2 - 2   e^{z} - z e^{z}}{3 z} ;]" title=" \sum_{k=0}^{\infty} \frac{z^{k}   \left(3 + k\right)}{\left(3 + 3 k\right) k!} = - \frac{2 - 2 e^{z} - z   e^{z}}{3 z} " src="http://fredrikj.net/blah/gif_015.gif"&gt;&lt;/p&gt;&lt;pre&gt;&amp;gt;&amp;gt;&amp;gt; simplify(hypsum((3+k)/(3+3*k)*z**k/fac(k), k))&lt;br /&gt;-(2 - 2*exp(z) - z*exp(z))/(3*z)&lt;br /&gt;&amp;gt;&amp;gt;&amp;gt; _.evalf(subs={d:'1/2',z:'1/4'})&lt;br /&gt;1.18540958339656&lt;br /&gt;&amp;gt;&amp;gt;&amp;gt; mpmath.nsum(lambda k: (3+k)/(3+3*k)*0.25**k/mpmath.fac(k), [0,mpmath.inf])&lt;br /&gt;1.18540958339656&lt;br /&gt;&lt;/pre&gt;&lt;p&gt;&lt;img style="display: inline;" alt="[; \sum_{k=0}^{\infty}   \frac{k^{2} z^{1 + 2 k}}{\left(9 + 2 k\right)!} = \frac{1}{39916800}   z^{3} \left(- \,_{1}F_{2}\left(2; 6,\frac{13}{2}; \frac{1}{4}   z^{2}\right) + 2 \,_{1}F_{2}\left(3; 6,\frac{13}{2}; \frac{1}{4}   z^{2}\right)\right) ;]" title=" \sum_{k=0}^{\infty} \frac{k^{2} z^{1 + 2   k}}{\left(9 + 2 k\right)!} = \frac{1}{39916800} z^{3} \left(-   \,_{1}F_{2}\left(2; 6,\frac{13}{2}; \frac{1}{4} z^{2}\right) + 2   \,_{1}F_{2}\left(3; 6,\frac{13}{2}; \frac{1}{4} z^{2}\right)\right) "   src="http://fredrikj.net/blah/gif_003.gif"&gt;&lt;/p&gt;&lt;pre&gt;&amp;gt;&amp;gt;&amp;gt; hypsum(k**2 * z**(2*k+1) / fac(2*k+9), k)&lt;br /&gt;z**3*(-1F2([2], [6, 13/2], z**2/4) + 2*1F2([3], [6, 13/2], z**2/4))/39916800&lt;br /&gt;&amp;gt;&amp;gt;&amp;gt; _.evalf(subs={d:'1/2',z:'1/4'})&lt;br /&gt;3.92066920165299e-10&lt;br /&gt;&amp;gt;&amp;gt;&amp;gt; mpmath.nsum(lambda k: k**2 * 0.25**(2*k+1) / mpmath.fac(2*k+9), [0,mpmath.inf])&lt;br /&gt;3.92066920165299e-10&lt;br /&gt;&lt;/pre&gt;&lt;p&gt;&lt;img style="display: inline;" alt="[; \sum_{k=0}^{\infty} z^{k}   \left(1 + k\right) \left(1 + 2 k\right) \left(2 + k\right) \left(3 +   k\right) = \frac{6 + 42 z}{1 - 5 z + 10 z^{2} - 10 z^{3} + 5 z^{4} -   z^{5}} ;]" title=" \sum_{k=0}^{\infty} z^{k} \left(1 + k\right) \left(1 +   2 k\right) \left(2 + k\right) \left(3 + k\right) = \frac{6 + 42 z}{1 - 5   z + 10 z^{2} - 10 z^{3} + 5 z^{4} - z^{5}} "   src="http://fredrikj.net/blah/gif_041.gif"&gt;&lt;/p&gt;&lt;pre&gt;&amp;gt;&amp;gt;&amp;gt; simplify(hypsum((k+1)*(k+2)*(k+3)*(1+2*k)*z**k, k))&lt;br /&gt;(6 + 42*z)/(1 - 5*z + 10*z**2 - 10*z**3 + 5*z**4 - z**5)&lt;br /&gt;&amp;gt;&amp;gt;&amp;gt; _.evalf(subs={d:'1/2',z:'1/4'})&lt;br /&gt;69.5308641975309&lt;br /&gt;&amp;gt;&amp;gt;&amp;gt; mpmath.nsum(lambda k: (k+1)*(k+2)*(k+3)*(1+2*k)*0.25**k, [0,mpmath.inf])&lt;br /&gt;69.5308641975309&lt;br /&gt;&lt;/pre&gt;&lt;p&gt;&lt;img style="display: inline;" alt=""   title=" \sum_{k=0}^{\infty} \frac{k^{3} \left(- z\right)^{k}}{1 + k} = -   \frac{1}{2} z \left(2 \frac{1 - z}{\left(1 + z\right)^{3}} -   \frac{\frac{2 + 2 z}{1 + z} - \frac{\left(2 + 2 z\right)   \operatorname{log}\left(1 + z\right)}{z}}{z \left(1 + z\right)} -   \frac{2}{\left(1 + z\right)^{2}}\right) " src="http://fredrikj.net/blah/gif_008.gif"&gt;&lt;/p&gt;&lt;pre&gt;&amp;gt;&amp;gt;&amp;gt; hypsum(k**3 * (-z)**k / (k+1), k)&lt;br /&gt;-z*(2*(1 - z)/(1 + z)**3 - ((2 + 2*z)/(1 + z) - (2 + 2*z)*log(1 + z)/z)/(z*(1 + z)) - 2/(1 + z)**2)/2&lt;br /&gt;&amp;gt;&amp;gt;&amp;gt; _.evalf(subs={d:'1/2',z:'1/4'})&lt;br /&gt;-0.0285742052568390&lt;br /&gt;&amp;gt;&amp;gt;&amp;gt; mpmath.nsum(lambda k: k**3 * (-0.25)**k / (k+1), [0,mpmath.inf])&lt;br /&gt;-0.028574205256839&lt;br /&gt;&lt;/pre&gt;&lt;p&gt;&lt;img style="display: inline;" alt="[; \sum_{k=0}^{\infty}   \frac{e^{k}}{k!} = e^{e} ;]" title=" \sum_{k=0}^{\infty}   \frac{e^{k}}{k!} = e^{e} " src="http://fredrikj.net/blah/gif_017.gif"&gt;&lt;/p&gt;&lt;pre&gt;&amp;gt;&amp;gt;&amp;gt; hypsum(E**k / fac(k), k)&lt;br /&gt;exp(E)&lt;br /&gt;&amp;gt;&amp;gt;&amp;gt; _.evalf()&lt;br /&gt;15.1542622414793&lt;br /&gt;&amp;gt;&amp;gt;&amp;gt; mpmath.nsum(lambda k: mpmath.e**k / mpmath.fac(k), [0,mpmath.inf])&lt;br /&gt;15.1542622414793&lt;br /&gt;&lt;/pre&gt;&lt;p&gt;&lt;img style="display: inline;" alt="[; \sum_{k=0}^{\infty}   \frac{\operatorname{cos}\left(k\right)}{k!} = \frac{1}{2}   e^{e^{\mathbf{\imath}}} + \frac{1}{2} e^{e^{- \mathbf{\imath}}} ;]"   title=" \sum_{k=0}^{\infty} \frac{\operatorname{cos}\left(k\right)}{k!} =   \frac{1}{2} e^{e^{\mathbf{\imath}}} + \frac{1}{2} e^{e^{-   \mathbf{\imath}}} " src="http://fredrikj.net/blah/gif_033.gif"&gt;&lt;/p&gt;&lt;pre&gt;&amp;gt;&amp;gt;&amp;gt; hypsum(cos(k) / fac(k), k)&lt;br /&gt;exp(exp(I))/2 + exp(exp(-I))/2&lt;br /&gt;&amp;gt;&amp;gt;&amp;gt; _.evalf()&lt;br /&gt;1.14383564379164 + .0e-20*I&lt;br /&gt;&amp;gt;&amp;gt;&amp;gt; mpmath.nsum(lambda k: mpmath.cos(k) / mpmath.fac(k), [0,mpmath.inf])&lt;br /&gt;1.14383564379164&lt;br /&gt;&lt;/pre&gt;&lt;p&gt;&lt;img style="display: inline;" alt="[; \sum_{k=0}^{\infty}   \frac{\operatorname{cos}\left(k\right)}{1 + 2 k} = \frac{1}{2}   \operatorname{atanh}\left(e^{- \frac{1}{2} \mathbf{\imath}}\right)   e^{\frac{1}{2} \mathbf{\imath}} + \frac{1}{2}   \operatorname{atanh}\left(e^{\frac{1}{2} \mathbf{\imath}}\right) e^{-   \frac{1}{2} \mathbf{\imath}} ;]" title=" \sum_{k=0}^{\infty}   \frac{\operatorname{cos}\left(k\right)}{1 + 2 k} = \frac{1}{2}   \operatorname{atanh}\left(e^{- \frac{1}{2} \mathbf{\imath}}\right)   e^{\frac{1}{2} \mathbf{\imath}} + \frac{1}{2}   \operatorname{atanh}\left(e^{\frac{1}{2} \mathbf{\imath}}\right) e^{-   \frac{1}{2} \mathbf{\imath}} " src="http://fredrikj.net/blah/gif_024.gif"&gt;&lt;/p&gt;&lt;pre&gt;&amp;gt;&amp;gt;&amp;gt; hypsum(cos(k) / (2*k+1), k)&lt;br /&gt;atanh(exp(-I/2))*exp(I/2)/2 + atanh(exp(I/2))*exp(-I/2)/2&lt;br /&gt;&amp;gt;&amp;gt;&amp;gt; _.evalf()&lt;br /&gt;0.975556628913311&lt;br /&gt;&amp;gt;&amp;gt;&amp;gt; mpmath.nsum(lambda k: mpmath.cos(k) / (2*k+1), [0,mpmath.inf])&lt;br /&gt;0.975556628913311&lt;br /&gt;&lt;/pre&gt;&lt;p&gt;&lt;img style="display: inline;" alt="[; \sum_{k=0}^{\infty}   \frac{k \operatorname{cos}\left(k\right)}{k!} = \frac{1}{2}   e^{\mathbf{\imath} + e^{\mathbf{\imath}}} + \frac{1}{2} e^{-   \mathbf{\imath} + e^{- \mathbf{\imath}}} ;]" title=" \sum_{k=0}^{\infty}   \frac{k \operatorname{cos}\left(k\right)}{k!} = \frac{1}{2}   e^{\mathbf{\imath} + e^{\mathbf{\imath}}} + \frac{1}{2} e^{-   \mathbf{\imath} + e^{- \mathbf{\imath}}} " src="http://fredrikj.net/blah/gif_036.gif"&gt;&lt;/p&gt;&lt;pre&gt;&amp;gt;&amp;gt;&amp;gt; simplify(hypsum(cos(k) * k / fac(k), k))&lt;br /&gt;exp(I + exp(I))/2 + exp(-I + exp(-I))/2&lt;br /&gt;&amp;gt;&amp;gt;&amp;gt; _.evalf()&lt;br /&gt;-0.458967373729452 + .0e-20*I&lt;br /&gt;&amp;gt;&amp;gt;&amp;gt; mpmath.nsum(lambda k: mpmath.cos(k) * k / mpmath.fac(k), [0,mpmath.inf])&lt;br /&gt;-0.458967373729452&lt;br /&gt;&lt;/pre&gt;&lt;p&gt;&lt;img style="display: inline;" alt="[; \sum_{k=0}^{\infty}   \frac{\left(-1\right)^{k}}{1 + 2 k} = \frac{1}{4} \pi ;]" title="   \sum_{k=0}^{\infty} \frac{\left(-1\right)^{k}}{1 + 2 k} = \frac{1}{4}   \pi " src="http://fredrikj.net/blah/gif_009.gif"&gt;&lt;/p&gt;&lt;pre&gt;&amp;gt;&amp;gt;&amp;gt; hypsum((-1)**k / (2*k+1), k)&lt;br /&gt;pi/4&lt;br /&gt;&amp;gt;&amp;gt;&amp;gt; _.evalf()&lt;br /&gt;0.785398163397448&lt;br /&gt;&amp;gt;&amp;gt;&amp;gt; mpmath.nsum(lambda k: (-1)**k / (2*k+1), [0,mpmath.inf])&lt;br /&gt;0.785398163397448&lt;br /&gt;&lt;/pre&gt;&lt;p&gt;&lt;img style="display: inline;" alt="[; \sum_{k=0}^{\infty}   \frac{\left(-1\right)^{k}}{6 + 2 k} = - \frac{1}{4} + \frac{1}{2}   \operatorname{log}\left(2\right) ;]" title=" \sum_{k=0}^{\infty}   \frac{\left(-1\right)^{k}}{6 + 2 k} = - \frac{1}{4} + \frac{1}{2}   \operatorname{log}\left(2\right) " src="http://fredrikj.net/blah/gif_006.gif"&gt;&lt;/p&gt;&lt;pre&gt;&amp;gt;&amp;gt;&amp;gt; hypsum((-1)**k / (2*k+6), k)&lt;br /&gt;-1/4 + log(2)/2&lt;br /&gt;&amp;gt;&amp;gt;&amp;gt; _.evalf()&lt;br /&gt;0.0965735902799727&lt;br /&gt;&amp;gt;&amp;gt;&amp;gt; mpmath.nsum(lambda k: (-1)**k / (2*k+6), [0,mpmath.inf])&lt;br /&gt;0.0965735902799727&lt;br /&gt;&lt;/pre&gt;&lt;p&gt;&lt;img style="display: inline;" alt="[; \sum_{k=0}^{\infty}   \frac{\left(-1\right)^{k}}{\left(1 + 2 k\right)^{2}} = C ;]" title="   \sum_{k=0}^{\infty} \frac{\left(-1\right)^{k}}{\left(1 + 2 k\right)^{2}}   = C " src="http://fredrikj.net/blah/gif_040.gif"&gt;&lt;/p&gt;&lt;pre&gt;&amp;gt;&amp;gt;&amp;gt; hypsum((-1)**k / (2*k+1)**2, k)&lt;br /&gt;Catalan&lt;br /&gt;&amp;gt;&amp;gt;&amp;gt; _.evalf()&lt;br /&gt;0.915965594177219&lt;br /&gt;&amp;gt;&amp;gt;&amp;gt; mpmath.nsum(lambda k: (-1)**k / (2*k+1)**2, [0,mpmath.inf])&lt;br /&gt;0.915965594177219&lt;br /&gt;&lt;/pre&gt;&lt;p&gt;&lt;img style="display: inline;" alt="[; \sum_{k=0}^{\infty}   \frac{\left(-1\right)^{k}}{\left(2 + 2 k\right)^{2}} = \frac{1}{48}   \pi^{2} ;]" title=" \sum_{k=0}^{\infty}   \frac{\left(-1\right)^{k}}{\left(2 + 2 k\right)^{2}} = \frac{1}{48}   \pi^{2} " src="http://fredrikj.net/blah/gif_030.gif"&gt;&lt;/p&gt;&lt;pre&gt;&amp;gt;&amp;gt;&amp;gt; hypsum((-1)**k / (2*k+2)**2, k)&lt;br /&gt;pi**2/48&lt;br /&gt;&amp;gt;&amp;gt;&amp;gt; _.evalf()&lt;br /&gt;0.205616758356028&lt;br /&gt;&amp;gt;&amp;gt;&amp;gt; mpmath.nsum(lambda k: (-1)**k / (2*k+2)**2, [0,mpmath.inf])&lt;br /&gt;0.205616758356028&lt;br /&gt;&lt;/pre&gt;&lt;p&gt;&lt;img style="display: inline;" alt="[; \sum_{k=0}^{\infty}   \frac{\left(-1\right)^{k} \left(1 + k\right)}{\left(3 + 2 k\right)^{2}} =   \frac{1}{9} \,_{3}F_{2}\left(\frac{3}{2},\frac{3}{2},2;   \frac{5}{2},\frac{5}{2}; -1\right) ;]" title=" \sum_{k=0}^{\infty}   \frac{\left(-1\right)^{k} \left(1 + k\right)}{\left(3 + 2 k\right)^{2}} =   \frac{1}{9} \,_{3}F_{2}\left(\frac{3}{2},\frac{3}{2},2;   \frac{5}{2},\frac{5}{2}; -1\right) " src="http://fredrikj.net/blah/gif_021.gif"&gt;&lt;/p&gt;&lt;pre&gt;&amp;gt;&amp;gt;&amp;gt; hypsum((-1)**k * (k+1) / (2*k+3)**2, k)&lt;br /&gt;3F2([3/2, 3/2, 2], [5/2, 5/2], -1)/9&lt;br /&gt;&amp;gt;&amp;gt;&amp;gt; _.evalf()&lt;br /&gt;0.0652837153898853&lt;br /&gt;&amp;gt;&amp;gt;&amp;gt; mpmath.nsum(lambda k: (-1)**k * (k+1) / (2*k+3)**2, [0,mpmath.inf])&lt;br /&gt;0.0652837153898854&lt;br /&gt;&lt;/pre&gt;&lt;p&gt;&lt;img style="display: inline;" alt="[; \sum_{k=0}^{\infty}   \frac{1}{4 + 2 k + k^{2}} = \frac{1}{6} \mathbf{\imath} \sqrt{3} \left(-   \operatorname{\psi}\left(0,1 + \mathbf{\imath} \sqrt{3}\right) +   \operatorname{\psi}\left(0,1 - \mathbf{\imath} \sqrt{3}\right)\right)   ;]" title=" \sum_{k=0}^{\infty} \frac{1}{4 + 2 k + k^{2}} = \frac{1}{6}   \mathbf{\imath} \sqrt{3} \left(- \operatorname{\psi}\left(0,1 +   \mathbf{\imath} \sqrt{3}\right) + \operatorname{\psi}\left(0,1 -   \mathbf{\imath} \sqrt{3}\right)\right) " src="http://fredrikj.net/blah/gif_026.gif"&gt;&lt;/p&gt;&lt;pre&gt;&amp;gt;&amp;gt;&amp;gt; hypsum(1/(k**2+2*k+4), k)&lt;br /&gt;I*3**(1/2)*(-polygamma(0, 1 + I*3**(1/2)) + polygamma(0, 1 - I*3**(1/2)))/6&lt;br /&gt;&amp;gt;&amp;gt;&amp;gt; _.evalf()&lt;br /&gt;0.740267076581851&lt;br /&gt;&amp;gt;&amp;gt;&amp;gt; mpmath.nsum(lambda k: 1/(k**2+2*k+4), [0,mpmath.inf])&lt;br /&gt;0.740267076581851&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;This work was possible thanks to the support of &lt;a href="http://www.nsf.gov/awardsearch/showAward.do?AwardNumber=0757627&amp;version=noscript"&gt;NSF grant DMS-0757627&lt;/a&gt;, which is gratefully acknowledged.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4754734402679928849-6264164165855848670?l=fredrik-j.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://fredrik-j.blogspot.com/feeds/6264164165855848670/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=4754734402679928849&amp;postID=6264164165855848670' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4754734402679928849/posts/default/6264164165855848670'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4754734402679928849/posts/default/6264164165855848670'/><link rel='alternate' type='text/html' href='http://fredrik-j.blogspot.com/2010/07/symbolic-infinite-series.html' title='Symbolic infinite series'/><author><name>Fredrik Johansson</name><uri>http://www.blogger.com/profile/01465400860530971858</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4754734402679928849.post-4011734447182517115</id><published>2010-06-21T17:53:00.009+02:00</published><updated>2010-06-22T10:24:47.936+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='sage'/><category scheme='http://www.blogger.com/atom/ns#' term='sympy'/><category scheme='http://www.blogger.com/atom/ns#' term='mpmath'/><title type='text'>Incomplete elliptic integrals complete</title><content type='html'>I'm very happy to finally have implemented incomplete elliptic integrals in mpmath (they were probably the most-requested missing special functions). Now mpmath can compute the three &lt;a href="http://en.wikipedia.org/wiki/Legendre_form"&gt;classical (Legendre) elliptic integrals&lt;/a&gt; &lt;i&gt;F&lt;/i&gt;(φ, &lt;i&gt;m&lt;/i&gt;), &lt;i&gt;E&lt;/i&gt;(φ, &lt;i&gt;m&lt;/i&gt;), Π(&lt;i&gt;n&lt;/i&gt;, φ, &lt;i&gt;m&lt;/i&gt;) as well &lt;a href="http://en.wikipedia.org/wiki/Carlson_symmetric_form"&gt;Carlson's symmetric integrals&lt;/a&gt; &lt;i&gt;R&lt;sub&gt;F&lt;/sub&gt;&lt;/i&gt;, &lt;i&gt;R&lt;sub&gt;C&lt;/sub&gt;&lt;/i&gt;, &lt;i&gt;R&lt;sub&gt;J&lt;/sub&gt;&lt;/i&gt;, &lt;i&gt;R&lt;sub&gt;D&lt;/sub&gt;&lt;/i&gt;, &lt;i&gt;R&lt;sub&gt;G&lt;/sub&gt;&lt;/i&gt;. Previously only the complete integrals &lt;i&gt;K&lt;/i&gt;(&lt;i&gt;m&lt;/i&gt;) and &lt;i&gt;E&lt;/i&gt;(&lt;i&gt;m&lt;/i&gt;) were available.&lt;br /&gt;&lt;br /&gt;The functions are called, respectively, &lt;tt&gt;ellipf&lt;/tt&gt;, &lt;tt&gt;ellipe&lt;/tt&gt;, &lt;tt&gt;ellippi&lt;/tt&gt;, &lt;tt&gt;elliprf&lt;/tt&gt;, &lt;tt&gt;elliprc&lt;/tt&gt;, &lt;tt&gt;elliprj&lt;/tt&gt;, &lt;tt&gt;elliprd&lt;/tt&gt;, &lt;tt&gt;elliprg&lt;/tt&gt;, although this could change before the release. For the code, see &lt;a href="http://code.google.com/p/mpmath/source/detail?r=1162"&gt;r1162&lt;/a&gt;, &lt;a href="http://code.google.com/p/mpmath/source/detail?r=1166"&gt;r1166&lt;/a&gt; and adjacent commits; documentation is available in the &lt;a href="http://mpmath.googlecode.com/svn/trunk/doc/build/functions/elliptic.html"&gt;elliptic functions&lt;/a&gt; section.&lt;br /&gt;&lt;br /&gt;This work was possible thanks to the support of &lt;a href="http://www.nsf.gov/awardsearch/showAward.do?AwardNumber=0757627&amp;version=noscript"&gt;NSF grant DMS-0757627&lt;/a&gt;, gratefully acknowledged.&lt;br /&gt;&lt;br /&gt;&lt;h4&gt;Elliptic integral basics&lt;/h4&gt;&lt;br /&gt;What are elliptic integrals and why are they important? As the name suggests, they are related to ellipses. Given an ellipse of width 2&lt;i&gt;a&lt;/i&gt; and height 2&lt;i&gt;b&lt;/i&gt;, i.e. satisfying&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://upload.wikimedia.org/math/9/4/b/94bc0fbfc1ace2e887afbc22be6d3520.png"&gt;&lt;img style="display: block; margin: 0px auto 10px; text-align: center; cursor: pointer; width: 102px; height: 43px;" src="http://upload.wikimedia.org/math/9/4/b/94bc0fbfc1ace2e887afbc22be6d3520.png" alt="" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;or using an angular coordinate &lt;i&gt;θ&lt;/i&gt;&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://upload.wikimedia.org/math/e/4/6/e46bfce48b17228de81bccafe94f9bba.png"&gt;&lt;img style="display: block; margin: 0px auto 10px; text-align: center; cursor: pointer; width: 93px; height: 42px;" src="http://upload.wikimedia.org/math/e/4/6/e46bfce48b17228de81bccafe94f9bba.png" alt="" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;the arc length along the ellipse from θ = 0 to θ = φ is given by&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://upload.wikimedia.org/math/7/2/e/72eb7117ada61031aabcaacf6c0b7d26.png"&gt;&lt;img style="display: block; margin: 0px auto 10px; text-align: center; cursor: pointer; width: 333px; height: 42px;" src="http://upload.wikimedia.org/math/7/2/e/72eb7117ada61031aabcaacf6c0b7d26.png" alt="" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;where &lt;i&gt;m&lt;/i&gt; = 1-(&lt;i&gt;a&lt;/i&gt;/&lt;i&gt;b&lt;/i&gt;)^2 is the so-called elliptic parameter.&lt;br /&gt;&lt;br /&gt;Here are some plots of half-ellipses of various proportions, and the corresponding arc lengths:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;a = 1&lt;br /&gt;b1 = 0.1; f1 = lambda x: b1*sqrt(1 - (x/a)**2)&lt;br /&gt;b2 = 0.6; f2 = lambda x: b2*sqrt(1 - (x/a)**2)&lt;br /&gt;b3 = 1.0; f3 = lambda x: b3*sqrt(1 - (x/a)**2)&lt;br /&gt;b4 = 2.0; f4 = lambda x: b4*sqrt(1 - (x/a)**2)&lt;br /&gt;plot([f1,f2,f3,f4], [-1,1])&lt;br /&gt;&lt;br /&gt;g1 = lambda phi: b1*ellipe(phi, 1-(a/b1)**2)&lt;br /&gt;g2 = lambda phi: b2*ellipe(phi, 1-(a/b2)**2)&lt;br /&gt;g3 = lambda phi: b3*ellipe(phi, 1-(a/b3)**2)&lt;br /&gt;g4 = lambda phi: b4*ellipe(phi, 1-(a/b4)**2)&lt;br /&gt;plot([g1,g2,g3,g4], [0,pi])&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://1.bp.blogspot.com/_rh0QblLk0C0/TB9u9z5cuII/AAAAAAAAAQg/MN1ciJQS3Co/s1600/el_3.png"&gt;&lt;img style="display: block; margin: 0px auto 10px; text-align: center; cursor: pointer; width: 400px; height: 394px;" src="http://1.bp.blogspot.com/_rh0QblLk0C0/TB9u9z5cuII/AAAAAAAAAQg/MN1ciJQS3Co/s400/el_3.png" alt="" id="BLOGGER_PHOTO_ID_5485224879342794882" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_rh0QblLk0C0/TB9tgNlJP4I/AAAAAAAAAQQ/m88UG4XCiZc/s1600/el_2.png"&gt;&lt;img style="display: block; margin: 0px auto 10px; text-align: center; cursor: pointer; width: 400px; height: 302px;" src="http://3.bp.blogspot.com/_rh0QblLk0C0/TB9tgNlJP4I/AAAAAAAAAQQ/m88UG4XCiZc/s400/el_2.png" alt="" id="BLOGGER_PHOTO_ID_5485223271329251202" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;The preceding formulas are sometimes seen with &lt;i&gt;a&lt;/i&gt; and &lt;i&gt;b&lt;/i&gt; switched, which simply corresponds to transposing &lt;i&gt;x&lt;/i&gt; and &lt;i&gt;y&lt;/i&gt; as the reference axis (above, the integration is assumed to start on the &lt;i&gt;x&lt;/i&gt; axis). One can readily confirm that &lt;i&gt;E&lt;/i&gt;(φ, &lt;i&gt;m&lt;/i&gt;) satisfies obvious geometric symmetries:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;&gt;&gt;&gt; a = 0.5&lt;br /&gt;&gt;&gt;&gt; b = 0.25&lt;br /&gt;&gt;&gt;&gt; m1 = 1-(a/b)**2&lt;br /&gt;&gt;&gt;&gt; m2 = 1-(b/a)**2&lt;br /&gt;&gt;&gt;&gt; b*ellipe(pi/2,m1)           # Arc length of quarter-ellipse&lt;br /&gt;0.6055280137842297624017815&lt;br /&gt;&gt;&gt;&gt; a*ellipe(pi/2,m2)&lt;br /&gt;0.6055280137842297624017815&lt;br /&gt;&gt;&gt;&gt; a*ellipe(pi/4,m2) + b*ellipe(pi/4,m1)&lt;br /&gt;0.6055280137842297624017815&lt;br /&gt;&gt;&gt;&gt; a*ellipe(pi/3,m2) + b*ellipe(pi/6,m1)&lt;br /&gt;0.6055280137842297624017815&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Elliptic integrals have countless uses in geometry and physics, and even in pure mathematics. They are related to Jacobi elliptic functions (already available in mpmath), which essentially are inverse functions of elliptic integrals:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;&gt;&gt;&gt; m = 0.5&lt;br /&gt;&gt;&gt;&gt; sn = ellipfun('sn')&lt;br /&gt;&gt;&gt;&gt; ellipf(asin(sn('0.65', m)), m)&lt;br /&gt;0.65&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;More information about elliptic integrals can be found in &lt;a href="http://dlmf.nist.gov/19"&gt;DLMF Chapter 19&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;&lt;h4&gt;Implementation&lt;/h4&gt;&lt;br /&gt;Implementing incomplete elliptic integrals properly is fairly complicated. When people have asked for them in the past, I just suggested using numerical integration. This works quite well most of the time, but it's inefficient (and possibly gives poor accuracy) at high precision, for large arguments, or near singularities. Another approach — computing incomplete integrals from Appell hypergeometric functions (available in mpmath) — basically has the same drawbacks.&lt;br /&gt;&lt;br /&gt;The numerically conscious way to compute elliptic integrals is to firstly exploit symmetries to obtain a small standard domain, and then using transformation formulas to reduce the magnitude of the arguments until the first few terms of the hypergeometric series give full accuracy (the &lt;a href="http://en.wikipedia.org/wiki/Arithmetic-geometric_mean"&gt;arithmetic-geometric mean&lt;/a&gt; for the complete integrals, which leads to some of the fastest ways to compute π, is a special case of this process).&lt;br /&gt;&lt;br /&gt;The transformations for Legendre's integrals are known as Landen's transformations. A more modern alternative, that I've chosen to use, is to represent Legendre's integrals in terms of Carlson's symmetric integrals which have much simpler structure. Although Carlson's integrals are mainly intended for internal use, I've exposed them as top-level functions since it wasn't much extra work and some users may well find them useful. (Carlson's forms seem to become increasingly standard.)&lt;br /&gt;&lt;br /&gt;&lt;a href="http://arxiv.org/abs/math/9409227v1"&gt;Carlson's paper&lt;/a&gt; is very readable and provides almost complete descriptions of the algorithms, including error bounds, which helped my implementation work tremendously. The error bounds are not rigorous in all cases, but good enough most of the time. Unfortunately, there are many special cases to consider, and Carlson does not address all of them explicitly, so putting it all together nevertheless involved a bit of work (and there are still some minor issues to fix).&lt;br /&gt;&lt;br /&gt;&lt;h4&gt;Complex branch structure&lt;/h4&gt;&lt;br /&gt;One major issue is how to define the elliptic integrals for complex (or in some places, negative) parameters. Because the integrands defining Legendre's elliptic integrals involve square roots of periodic functions, they have very complicated branch structure. I have chosen the periodic extension of the branches resulting naturally from use of the Carlson forms on -π/2 &lt; Re(&lt;i&gt;z&lt;/i&gt;) &lt; &amp;pi;/2. I &lt;i&gt;believe&lt;/i&gt; this is equivalent to choosing the principal-branch square root in the integrand and integrating along a path where the principal branch is continuous.&lt;br /&gt;&lt;br /&gt;For example, consider &lt;i&gt;E&lt;/i&gt;(φ, 3+4i). The integrand (with the principal square root, as computed by &lt;tt&gt;sqrt&lt;/tt&gt;) and the elliptic integral computed by &lt;tt&gt;ellipe&lt;/tt&gt; are plotted below:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&gt;&gt;&gt; cplot(lambda z: sqrt(1-(3+4j)*sin(z)**2), points=50000)&lt;/pre&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://1.bp.blogspot.com/_rh0QblLk0C0/TB-WCM2LNRI/AAAAAAAAAQw/SaTwWHZi-OQ/s1600/intebranch.png"&gt;&lt;img style="display: block; margin: 0px auto 10px; text-align: center; cursor: pointer; width: 400px; height: 302px;" src="http://1.bp.blogspot.com/_rh0QblLk0C0/TB-WCM2LNRI/AAAAAAAAAQw/SaTwWHZi-OQ/s400/intebranch.png" alt="" id="BLOGGER_PHOTO_ID_5485267835712910610" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&gt;&gt;&gt; cplot(lambda z: ellipe(z, 3+4j), points=50000)&lt;/pre&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://1.bp.blogspot.com/_rh0QblLk0C0/TB-WBuSpCRI/AAAAAAAAAQo/gDO-I0NBXss/s1600/ellipbranch.png"&gt;&lt;img style="display: block; margin: 0px auto 10px; text-align: center; cursor: pointer; width: 400px; height: 302px;" src="http://1.bp.blogspot.com/_rh0QblLk0C0/TB-WBuSpCRI/AAAAAAAAAQo/gDO-I0NBXss/s400/ellipbranch.png" alt="" id="BLOGGER_PHOTO_ID_5485267827510806802" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;It can be seen that the cuts have the same shape in both images (the branch points are necessarily the same). As the following figure shows, if we are to integrate from &lt;i&gt;z&lt;/i&gt; = 0 to &lt;i&gt;z&lt;/i&gt; = -2+4i (X), a straight path (black) will be bad as it crosses a branch cut (O). However, the modified path (blue) avoiding the branch cut by passing through &lt;i&gt;z&lt;/i&gt; = -2 is fine:&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://1.bp.blogspot.com/_rh0QblLk0C0/TB-XVIazi4I/AAAAAAAAAQ4/uxBZzLYLX9E/s1600/branchint.png"&gt;&lt;img style="display: block; margin: 0px auto 10px; text-align: center; cursor: pointer; width: 400px; height: 302px;" src="http://1.bp.blogspot.com/_rh0QblLk0C0/TB-XVIazi4I/AAAAAAAAAQ4/uxBZzLYLX9E/s400/branchint.png" alt="" id="BLOGGER_PHOTO_ID_5485269260453514114" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;We can confirm this with numerical integration:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;&gt;&gt;&gt; ellipe(-2+4j, 3+4j)&lt;br /&gt;(36.45544164923053561755261 + 49.28080768743760310823023j)&lt;br /&gt;&gt;&gt;&gt; quad(lambda z: sqrt(1-(3+4j)*sin(z)**2), [0,-2+4j])&lt;br /&gt;(19.65109997738606076596704 + 46.63216059660102110020723j)&lt;br /&gt;&gt;&gt;&gt; quad(lambda z: sqrt(1-(3+4j)*sin(z)**2), [0,-2,-2+4j])&lt;br /&gt;(36.45544164923053561755261 + 49.28080768743760310823023j)&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Unfortunately, Mathematica doesn't use quite the same branch cuts for its elliptic integrals. I don't have Mathematica available to create a plot for comparison purposes, but I think it basically uses vertical cuts instead of the curvilinear cuts seen in the images above. There are probably good reasons for doing this, presumably that it simplifies symbolic definite integration, although it seems to complicate the evaluation and formulaic representation of the functions. It would perhaps be good to support both conventions.&lt;br /&gt;&lt;br /&gt;As far as the symmetric functions are concerned, they have the nice property that although they involve non-principal square roots (chosen so as to be continuous along the real line), Carlson's algorithm gives the continuous branch automatically. For example, if we consider the first function&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_rh0QblLk0C0/TB_SFqcsdqI/AAAAAAAAARA/SkqOlZizfrg/s1600/2ce2deb0b65b3721d5d863917d10ae5e3d7b3da7.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 335px; height: 48px;" src="http://4.bp.blogspot.com/_rh0QblLk0C0/TB_SFqcsdqI/AAAAAAAAARA/SkqOlZizfrg/s400/2ce2deb0b65b3721d5d863917d10ae5e3d7b3da7.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5485333865896375970" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;with &lt;i&gt;x&lt;/i&gt; = i-1, &lt;i&gt;y&lt;/i&gt; = i, &lt;i&gt;z&lt;/i&gt; = 0, then as the following plot shows, the principal branch of the integrand has a discontinuity at &lt;i&gt;t&lt;/i&gt; = 1/2:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;&gt;&gt;&gt; x,y,z = j-1,j,0&lt;br /&gt;&gt;&gt;&gt; f = lambda t: 1/sqrt((t+x)*(t+y)*(t+z))&lt;br /&gt;&gt;&gt;&gt; plot(f, [0,3])&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_rh0QblLk0C0/TB_Te7oTyeI/AAAAAAAAARQ/6v4qfHP4o-k/s1600/wrong.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 400px; height: 302px;" src="http://4.bp.blogspot.com/_rh0QblLk0C0/TB_Te7oTyeI/AAAAAAAAARQ/6v4qfHP4o-k/s400/wrong.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5485335399516850658" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;To obtain the correct integrand, we switch to the negative square root on the left of the discontinuity (the positive sign should be used on the right because, for continuity with real parameters, we want the principal square root as &lt;i&gt;t&lt;/i&gt; &amp;rarr; +&amp;infin;):&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;&gt;&gt;&gt; g = lambda t: f(t) if t &gt;= 0.5 else -f(t)&lt;br /&gt;&gt;&gt;&gt; plot(g, [0,3])&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://1.bp.blogspot.com/_rh0QblLk0C0/TB_TZEw7iII/AAAAAAAAARI/RgehkM8Kais/s1600/right.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 400px; height: 302px;" src="http://1.bp.blogspot.com/_rh0QblLk0C0/TB_TZEw7iII/AAAAAAAAARI/RgehkM8Kais/s400/right.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5485335298889713794" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;A quick test shows that &lt;i&gt;f&lt;/i&gt; is wrong and &lt;i&gt;g&lt;/i&gt; is right:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;&gt;&gt;&gt; extradps(25)(quad)(f, [0,inf])/2&lt;br /&gt;(1.110136183412179775397387 - 0.04278194644898641314159222j)&lt;br /&gt;&gt;&gt;&gt; extradps(25)(quad)(g, [0,inf])/2&lt;br /&gt;(0.7961258658423391329305694 - 1.213856669836495986430094j)&lt;br /&gt;&gt;&gt;&gt; elliprf(x,y,z)&lt;br /&gt;(0.7961258658423391329305694 - 1.213856669836495986430094j)&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;In summary, branch cuts of special functions are a tricky business, and standards don't always exist, so both developers and users need to be careful working with them.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4754734402679928849-4011734447182517115?l=fredrik-j.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://fredrik-j.blogspot.com/feeds/4011734447182517115/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=4754734402679928849&amp;postID=4011734447182517115' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4754734402679928849/posts/default/4011734447182517115'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4754734402679928849/posts/default/4011734447182517115'/><link rel='alternate' type='text/html' href='http://fredrik-j.blogspot.com/2010/06/incomplete-elliptic-integrals-complete.html' title='Incomplete elliptic integrals complete'/><author><name>Fredrik Johansson</name><uri>http://www.blogger.com/profile/01465400860530971858</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://1.bp.blogspot.com/_rh0QblLk0C0/TB9u9z5cuII/AAAAAAAAAQg/MN1ciJQS3Co/s72-c/el_3.png' height='72' width='72'/><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4754734402679928849.post-6137742059412297894</id><published>2010-06-15T22:56:00.008+02:00</published><updated>2010-06-16T00:36:48.801+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='sage'/><category scheme='http://www.blogger.com/atom/ns#' term='sympy'/><category scheme='http://www.blogger.com/atom/ns#' term='mpmath'/><title type='text'>Assorted special functions update</title><content type='html'>Over the week-and-a-half since the last blog update, I've gotten a bunch more work done on special functions in mpmath.&lt;br /&gt;&lt;br /&gt;&lt;h3&gt;Function plots in the documentation&lt;/h3&gt;&lt;br /&gt;I have started adding graphics of special functions to the documentation. So far, I've done most of the &lt;a href="http://mpmath.googlecode.com/svn/trunk/doc/build/functions/bessel.html"&gt;Bessel-type functions&lt;/a&gt; and &lt;a href="http://mpmath.googlecode.com/svn/trunk/doc/build/functions/orthogonal.html"&gt;orthogonal polynomials&lt;/a&gt;. Many more to come!&lt;br /&gt;&lt;br /&gt;Example screenshot (see the Bessel functions page):&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://2.bp.blogspot.com/_rh0QblLk0C0/TBf6lXNz8ZI/AAAAAAAAAP4/TcGzbf9cTcE/s1600/cscreen.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 400px; height: 303px;" src="http://2.bp.blogspot.com/_rh0QblLk0C0/TBf6lXNz8ZI/AAAAAAAAAP4/TcGzbf9cTcE/s400/cscreen.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5483126591140917650" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;h3&gt;New inhomogeneous Bessel functions&lt;/h3&gt;&lt;br /&gt;There exists a large number of lesser-known special functions which are essentially variations of Bessel functions. These include functions which solve the generalized (inhomogeneous) Bessel differential equation&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_rh0QblLk0C0/TBfquKMhwwI/AAAAAAAAAPg/yCjwAW-N1_A/s1600/a1662c96aafc05aeede9f82b201ac0bd.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 336px; height: 23px;" src="http://3.bp.blogspot.com/_rh0QblLk0C0/TBfquKMhwwI/AAAAAAAAAPg/yCjwAW-N1_A/s400/a1662c96aafc05aeede9f82b201ac0bd.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5483109150078649090" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;with some specific right-hand side &lt;i&gt;g&lt;/i&gt;(&lt;i&gt;z&lt;/i&gt;). New additions to mpmath in this category are the Anger function (&lt;tt&gt;angerj()&lt;/tt&gt;), Weber function (&lt;tt&gt;webere()&lt;/tt&gt;), and Lommel functions (&lt;tt&gt;lommels1()&lt;/tt&gt;, &lt;tt&gt;lommels2()&lt;/tt&gt;). See commits &lt;a href="http://code.google.com/p/mpmath/source/detail?r=1143"&gt;here&lt;/a&gt; and &lt;a href="http://code.google.com/p/mpmath/source/detail?r=1144"&gt;here&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;More information about &lt;a href="http://dlmf.nist.gov/11.10"&gt;Anger-Weber functions&lt;/a&gt; and &lt;a href="http://dlmf.nist.gov/11.9"&gt;Lommel functions&lt;/a&gt; can be found in the DLMF.&lt;br /&gt;&lt;br /&gt;In the near future, I will probably further improve the implementations of the main Bessel functions. The Bessel functions are mostly implemented as generic hypergeometric functions, but the standard cases can be tuned a great deal with special-purpose code.&lt;br /&gt;&lt;br /&gt;&lt;h3&gt;Airy functions and related functions&lt;/h3&gt;&lt;br /&gt;The Airy functions Ai and Bi have been present for quite some time in mpmath. In a &lt;a href="http://code.google.com/p/mpmath/source/detail?r=1149"&gt;recent commit&lt;/a&gt;, I have rewritten them for improved rigor and better performance at high precision. There are also some new features, such as the ability to evaluate derivatives or iterated integrals of arbitrary order.&lt;br /&gt;&lt;br /&gt;Derivatives:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;&gt;&gt;&gt; from mpmath import *&lt;br /&gt;&gt;&gt;&gt; mp.dps = 25; mp.pretty = True&lt;br /&gt;&gt;&gt;&gt; airyai(1.5, derivative=5)&lt;br /&gt;0.211387453153454489799743&lt;br /&gt;&gt;&gt;&gt; diff(airyai, 1.5, 5)&lt;br /&gt;0.211387453153454489799743&lt;br /&gt;&gt;&gt;&gt; airyai(1.5, derivative=100)&lt;br /&gt;-6.480220187791312407132043e+49&lt;br /&gt;&gt;&gt;&gt; airybi(0, derivative=1000)&lt;br /&gt;3.754976097101270163249629e+854&lt;br /&gt;&gt;&gt;&gt; airybi(0, derivative=1001)&lt;br /&gt;0.0&lt;br /&gt;&gt;&gt;&gt; airybi(0, derivative=1002)&lt;br /&gt;3.756228172694934424506624e+856&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Integrals:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;&gt;&gt;&gt; airyai(5, derivative=-1)&lt;br /&gt;0.3332875903059178794866562&lt;br /&gt;&gt;&gt;&gt; quad(airyai, [0,5])&lt;br /&gt;0.3332875903059178794866562&lt;br /&gt;&gt;&gt;&gt; airyai(-100000, derivative=-1)&lt;br /&gt;-0.6665753658794626398413214&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Also, functions for computing the zeros of Ai and Bi (and the first derivatives) have been added:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;&gt;&gt;&gt; airyaizero(1)&lt;br /&gt;-2.338107410459767038489197&lt;br /&gt;&gt;&gt;&gt; airyaizero(2)&lt;br /&gt;-4.087949444130970616636989&lt;br /&gt;&gt;&gt;&gt; airybizero(1)&lt;br /&gt;-1.17371322270912792491998&lt;br /&gt;&gt;&gt;&gt; airybizero(1, derivative=1)&lt;br /&gt;-2.294439682614123246622459&lt;br /&gt;&gt;&gt;&gt; airybizero(1, derivative=1, complex=True)&lt;br /&gt;(0.2149470745374305676088329 + 1.100600143302797880647194j)&lt;br /&gt;&gt;&gt;&gt; airybizero(10000)&lt;br /&gt;-1304.584974702601410702964&lt;br /&gt;&gt;&gt;&gt; airybizero(10000, complex=True)&lt;br /&gt;(652.3059222438076432024695 + 1129.846189716375208308414j)&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;I have also implemented two new functions related to Airy functions: the &lt;a href="http://dlmf.nist.gov/9.12"&gt;Scorer functions&lt;/a&gt; Gi and Hi. These are available as &lt;tt&gt;scorergi()&lt;/tt&gt; and &lt;tt&gt;scorerhi()&lt;/tt&gt; respectively.&lt;br /&gt;&lt;br /&gt;Here are two plots of the Gi-function, which can also be &lt;a href="http://mpmath.googlecode.com/svn/trunk/doc/build/functions/bessel.html#scorergi"&gt;seen&lt;/a&gt; in the documentation:&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_rh0QblLk0C0/TBfv138DUzI/AAAAAAAAAPo/pjgZe8wsj6M/s1600/gi.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 360px; height: 270px;" src="http://4.bp.blogspot.com/_rh0QblLk0C0/TBfv138DUzI/AAAAAAAAAPo/pjgZe8wsj6M/s400/gi.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5483114780174799666" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://2.bp.blogspot.com/_rh0QblLk0C0/TBfv2B9ggBI/AAAAAAAAAPw/d0gsfBWOXDw/s1600/gi_c.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 360px; height: 270px;" src="http://2.bp.blogspot.com/_rh0QblLk0C0/TBfv2B9ggBI/AAAAAAAAAPw/d0gsfBWOXDw/s400/gi_c.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5483114782865260562" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;h3&gt;Interval gamma functions&lt;/h3&gt;&lt;br /&gt;The interval arithmetic context now implements &lt;tt&gt;gamma&lt;/tt&gt;, &lt;tt&gt;rgamma&lt;/tt&gt; (reciprocal gamma function), &lt;tt&gt;factorial&lt;/tt&gt; as well as &lt;tt&gt;loggamma&lt;/tt&gt; for real as well as complex arguments (&lt;a href="http://code.google.com/p/mpmath/source/detail?r=1147"&gt;commit&lt;/a&gt;). For example:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;&gt;&gt;&gt; iv.dps = 10&lt;br /&gt;&gt;&gt;&gt; iv.gamma('50.3')&lt;br /&gt;[1.96282982095908e+63, 1.96282982457481e+63]&lt;br /&gt;&gt;&gt;&gt; iv.gamma(iv.mpc('2.7','5.9'))&lt;br /&gt;([0.00269836072064322, 0.00269836072271801] + &lt;br /&gt; [0.0120124287790304, 0.0120124287810768]*j)&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;As a "practical" example, consider evaluating the &lt;a href="http://en.wikipedia.org/wiki/Riemann%E2%80%93Siegel_theta_function"&gt;Riemann-Siegel theta function&lt;/a&gt; which involves computing the difference of two log-gamma functions. For input with a large real part, the imaginary part in the result suffers from massive cancellation and may end up with the wrong sign:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;&gt;&gt;&gt; mp.dps = 15&lt;br /&gt;&gt;&gt;&gt; mp.siegeltheta(10**50 + 0.25j)&lt;br /&gt;(5.61456887916465e+51 - 0.143091235731175j)&lt;br /&gt;&gt;&gt;&gt; mp.dps = 10; nprint(mp.siegeltheta(10**50 + 0.25j).imag)&lt;br /&gt;-0.143091&lt;br /&gt;&gt;&gt;&gt; mp.dps = 100; nprint(mp.siegeltheta(10**50 + 0.25j).imag)&lt;br /&gt;14.1614&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;With interval arithmetic, the sign uncertainty is reflected in the output:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;&gt;&gt;&gt; iv.dps = 15&lt;br /&gt;&gt;&gt;&gt; iv.siegeltheta(10**50 + 0.25j)&lt;br /&gt;([5.6145688791646467648e+51, 5.6145688791646474294e+51] +&lt;br /&gt;[-5.0706024009129187319e+30, 5.070602400912917606e+30]*j)&lt;br /&gt;&gt;&gt;&gt; iv.dps = 50&lt;br /&gt;&gt;&gt;&gt; iv.siegeltheta(10**50 + 0.25j)&lt;br /&gt;([5614568879164647368060513633451316140100495086670736.0, 5614568879164647368060&lt;br /&gt;513633451316140100495086670744.0] + &lt;br /&gt;[14.1613521236438249782320715810808676610440&lt;br /&gt;8814838558203, 14.16147419395632497823207158108086766104408814838560341]*j)&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;As another example, consider evaluating the gamma function of a huge argument. The digits in the answer may be "wrong" because the input is converted from decimal to binary, and the gamma function is sensitive to the input being perturbed:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;&gt;&gt;&gt; mp.dps = 15&lt;br /&gt;&gt;&gt;&gt; mp.gamma('123456789012345.1')&lt;br /&gt;6.11544992055093e+1686076589184486&lt;br /&gt;&gt;&gt;&gt; mp.dps = 30&lt;br /&gt;&gt;&gt;&gt; mp.gamma('123456789012345.1')&lt;br /&gt;7.49032018540342193592769680745e+1686076589184486&lt;br /&gt;&gt;&gt;&gt; mp.dps = 60&lt;br /&gt;&gt;&gt;&gt; mp.gamma('123456789012345.1')&lt;br /&gt;7.49032018540342058679709881225047421518964527875047787194339e+1686076589184486&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;With interval arithmetic, the uncertainty in the input is propagated correctly:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;&gt;&gt;&gt; iv.dps = 15&lt;br /&gt;&gt;&gt;&gt; iv.nprint(iv.gamma('123456789012345.1'), mode='diff')&lt;br /&gt;[6.11545e+1686076589184486, 1.01533e+1686076589184487]&lt;br /&gt;&gt;&gt;&gt; iv.dps = 30&lt;br /&gt;&gt;&gt;&gt; iv.nprint(iv.gamma('123456789012345.1'), 20, mode='diff')&lt;br /&gt;7.4903201854034[185631, 219359]e+1686076589184486&lt;br /&gt;&gt;&gt;&gt; iv.dps = 60&lt;br /&gt;&gt;&gt;&gt; iv.nprint(iv.gamma('123456789012345.1'), 50, mode='diff')&lt;br /&gt;7.49032018540342058679709881225047421518964527[60898, 87505]e+1686076589184486&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;h3&gt;Rewritten Lambert W function&lt;/h3&gt;&lt;br /&gt;Lastly, the Lambert W function has received a much-needed rewrite (&lt;a href="http://code.google.com/p/mpmath/source/detail?r=1158"&gt;commit&lt;/a&gt;) mainly to improve evaluation very close to the branch cut along the negative axis and particularly near the branch point at -1/&lt;i&gt;e&lt;/i&gt; for the &lt;i&gt;k&lt;/i&gt; = -1, 0, 1 branches.&lt;br /&gt;&lt;br /&gt;With the previous implementation, results were frequently inaccurate or ended up on the wrong branch in this region. Here are some hard cases that now work perfectly:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;&gt;&gt;&gt; mp.dps = 1000&lt;br /&gt;&gt;&gt;&gt; x = -1/e + mpf('1e-900')&lt;br /&gt;&gt;&gt;&gt; y = -1/e - mpf('1e-900')&lt;br /&gt;&gt;&gt;&gt; z = -1/e + mpf('1e-900')*1j&lt;br /&gt;&gt;&gt;&gt; w = -1/e - mpf('1e-900')*1j&lt;br /&gt;&gt;&gt;&gt; mp.dps = 25&lt;br /&gt;&gt;&gt;&gt; lambertw(x,0); lambertw(y,0); lambertw(z,0); lambertw(w,0)&lt;br /&gt;-1.0&lt;br /&gt;(-1.0 + 2.331643981597124203363536e-450j)&lt;br /&gt;(-1.0 + 1.648721270700128146848651e-450j)&lt;br /&gt;(-1.0 - 1.648721270700128146848651e-450j)&lt;br /&gt;&gt;&gt;&gt; lambertw(x,1); lambertw(y,1); lambertw(z,1); lambertw(w,1)&lt;br /&gt;(-3.088843015613043855957087 + 7.461489285654254556906117j)&lt;br /&gt;(-3.088843015613043855957087 + 7.461489285654254556906117j)&lt;br /&gt;(-3.088843015613043855957087 + 7.461489285654254556906117j)&lt;br /&gt;(-1.0 + 1.648721270700128146848651e-450j)&lt;br /&gt;&gt;&gt;&gt; lambertw(x,-1); lambertw(y,-1); lambertw(z,-1); lambertw(w,-1)&lt;br /&gt;-1.0&lt;br /&gt;(-1.0 - 2.331643981597124203363536e-450j)&lt;br /&gt;(-1.0 - 1.648721270700128146848651e-450j)&lt;br /&gt;(-3.088843015613043855957087 - 7.461489285654254556906117j)&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;To finish this post, I present the following Mathematica bug:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;remote1:frejohl:[~]$ math&lt;br /&gt;Mathematica 7.0 for Linux x86 (32-bit)&lt;br /&gt;Copyright 1988-2008 Wolfram Research, Inc.&lt;br /&gt;&lt;br /&gt;In[1]:= Im[LambertW[0,-1/E-10^(-900)]]&lt;br /&gt;&lt;br /&gt;Out[1]= 0&lt;br /&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4754734402679928849-6137742059412297894?l=fredrik-j.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://fredrik-j.blogspot.com/feeds/6137742059412297894/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=4754734402679928849&amp;postID=6137742059412297894' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4754734402679928849/posts/default/6137742059412297894'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4754734402679928849/posts/default/6137742059412297894'/><link rel='alternate' type='text/html' href='http://fredrik-j.blogspot.com/2010/06/assorted-special-functions-update.html' title='Assorted special functions update'/><author><name>Fredrik Johansson</name><uri>http://www.blogger.com/profile/01465400860530971858</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://2.bp.blogspot.com/_rh0QblLk0C0/TBf6lXNz8ZI/AAAAAAAAAP4/TcGzbf9cTcE/s72-c/cscreen.png' height='72' width='72'/><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4754734402679928849.post-234420875625644251</id><published>2010-06-06T15:18:00.006+02:00</published><updated>2010-06-06T17:08:07.846+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='sage'/><category scheme='http://www.blogger.com/atom/ns#' term='sympy'/><category scheme='http://www.blogger.com/atom/ns#' term='mpmath'/><title type='text'>Announcing mpmath 0.15</title><content type='html'>I'm happy to announce the release of &lt;a href="http://code.google.com/p/mpmath/"&gt;mpmath 0.15&lt;/a&gt;!&lt;br /&gt;&lt;br /&gt;This should've happened earlier, but I was obstructed by other obligations (in particular, finishing my master's thesis). The good news is that I will be working full-time on special functions in mpmath and Sage again this summer thanks to sponsorship provided by &lt;a href="http://wstein.org/"&gt;William Stein&lt;/a&gt; (with money from an &lt;a href="http://www.nsf.gov/awardsearch/showAward.do?AwardNumber=0757627"&gt;NSF grant&lt;/a&gt;). I will also come to &lt;a href="http://www.lorentzcenter.nl/lc/web/2010/415/info.php3?wsid=415"&gt;Sage Days 23&lt;/a&gt; (July 5-9, Leiden, the Netherlands) and &lt;a href="http://wiki.sagemath.org/days24"&gt;Sage Days 24&lt;/a&gt; (July 17-22, Linz, Austria) which should be as fun as &lt;a href="http://fredrik-j.blogspot.com/2009/05/report-from-sage-days-15.html"&gt;SD15&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;What's new in mpmath 0.15? As usual, the details can be found in the &lt;a href="http://mpmath.googlecode.com/svn/trunk/CHANGES"&gt;CHANGES&lt;/a&gt; file and the &lt;a href="http://code.google.com/p/mpmath/source/list"&gt;list of commits&lt;/a&gt;. Most major changes were covered in detail in previous blog posts, so I will first of all simply link to those posts along with short summaries:&lt;br /&gt;&lt;br /&gt;&lt;a href="http://fredrik-j.blogspot.com/2010/03/speedups-of-elementary-functions.html"&gt;Speedups of elementary functions&lt;/a&gt; - cos, sin, atan, cosh, sinh, exp and all derived functions are now faster. Of particular note, trigonometric functions use an asymptotically faster algorithm (all elementary functions have similar asymptotic performance now).&lt;br /&gt;&lt;a href="http://fredrik-j.blogspot.com/2010/02/new-gamma-function-implementation.html"&gt;&lt;br /&gt;A new gamma function implementation&lt;/a&gt; - the gamma function and log-gamma functions have been rewritten scratch, using faster algorithms and code optimizations. The new versions are uniformly faster than the old ones, and tens or hundreds of times faster in many important situations. They are also more accurate in some special cases (near singularities, for complex arguments with extremely small real part, ...).&lt;br /&gt;&lt;br /&gt;&lt;a href="http://fredrik-j.blogspot.com/2010/02/numerical-multidimensional-infinite.html"&gt;Numerical multidimensional infinite series&lt;/a&gt; - &lt;tt&gt;nsum()&lt;/tt&gt; can now evaluate series (finite or infinite) in any number of dimensions&lt;br /&gt;&lt;br /&gt;&lt;a href="http://fredrik-j.blogspot.com/2010/03/computing-large-zeta-zeros-with-mpmath.html"&gt;Computing large zeta zeros with mpmath&lt;/a&gt; - &lt;a href="http://personal.us.es/arias/"&gt;Juan Arias de Reyna&lt;/a&gt; has implemented code for computing the &lt;i&gt;n&lt;/i&gt;th zero of the Riemann zeta function on the critical line for arbitrarily large &lt;i&gt;n&lt;/i&gt;. On a related note, the Riemann zeta function code has been optimized (mostly through elimination of low-level overhead) to speed up such computations.&lt;br /&gt;&lt;br /&gt;&lt;a href="http://fredrik-j.blogspot.com/2010/03/hypergeneralization.html"&gt;Hypergeneralization&lt;/a&gt; - generalized 2D hypergeometric series, bilateral hypergeometric series and some &lt;i&gt;q&lt;/i&gt;-analogs (&lt;i&gt;q&lt;/i&gt;-Pochhammer symbol, &lt;i&gt;q&lt;/i&gt;-factorial, &lt;i&gt;q&lt;/i&gt;-gamma function, &lt;i&gt;q&lt;/i&gt;-hypergeometric series) have been implemented.&lt;br /&gt;&lt;br /&gt;In addition, there are many changes that I haven't had time to blog about yet. I will now write briefly about some of these:&lt;br /&gt;&lt;br /&gt;&lt;h3&gt;Elliptic functions&lt;/h3&gt;&lt;br /&gt;The support for working with &lt;a href="http://mpmath.googlecode.com/svn/trunk/doc/build/functions/elliptic.html"&gt;elliptic functions&lt;/a&gt; has been improved. Instead of just the standard three, all 12 &lt;a href="http://en.wikipedia.org/wiki/Jacobi%27s_elliptic_functions"&gt;Jacobi elliptic functions&lt;/a&gt; are now available via &lt;tt&gt;ellipfun&lt;/tt&gt;, e.g. the cd-function:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;&gt;&gt;&gt; from mpmath import *&lt;br /&gt;&gt;&gt;&gt; mp.dps = 25; mp.pretty = True&lt;br /&gt;&gt;&gt;&gt; ellipfun('cd', 3.5, 0.5)&lt;br /&gt;-0.9891101840595543931308394&lt;br /&gt;&gt;&gt;&gt; cd = ellipfun('cd')&lt;br /&gt;&gt;&gt;&gt; cd(3.5, 0.5)&lt;br /&gt;-0.9891101840595543931308394&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Functions &lt;tt&gt;qfrom()&lt;/tt&gt;, &lt;tt&gt;qbarfrom()&lt;/tt&gt;, &lt;tt&gt;mfrom()&lt;/tt&gt;, &lt;tt&gt;kfrom()&lt;/tt&gt;, &lt;tt&gt;taufrom()&lt;/tt&gt; have also been added to convert between the various forms of arguments to elliptic functions (nomes (two definitions), parameters, moduli, half-period ratios). If you're like me and never can remember such formulas, let alone avoid messing up when trying to apply them, this can be quite convenient. Some functions (in particular, &lt;tt&gt;ellipfun&lt;/tt&gt; also support direct choice of convention using keyword arguments):&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;&gt;&gt;&gt; ellipfun('sn',2,1+1j)    # default is m&lt;br /&gt;(1.333680690847080418060154 - 0.2084318767795699518406447j)&lt;br /&gt;&gt;&gt;&gt; ellipfun('sn',2,q=qfrom(m=1+1j))&lt;br /&gt;(1.333680690847080418060154 - 0.2084318767795699518406447j)&lt;br /&gt;&gt;&gt;&gt; ellipfun('sn',2,k=kfrom(m=1+1j))&lt;br /&gt;(1.333680690847080418060154 - 0.2084318767795699518406447j)&lt;br /&gt;&gt;&gt;&gt; ellipfun('sn',2,tau=taufrom(m=1+1j))&lt;br /&gt;(1.333680690847080418060154 - 0.2084318767795699518406447j)&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Another new function is the &lt;a href="http://en.wikipedia.org/wiki/J-invariant"&gt;Klein j-invariant&lt;/a&gt; (or rather, the "absolute invariant" or "J-function" which differs by a constant factor, and also is the normalization used by Mathematica). A nice application is to compute the Laurent series expansion in terms of the (number-theoretic) nome, giving a famous &lt;a href="http://www.research.att.com/~njas/sequences/A000521"&gt;integer sequence&lt;/a&gt;:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;&gt;&gt;&gt; mp.dps = 15&lt;br /&gt;&gt;&gt;&gt; taylor(lambda q: 1728*q*kleinj(qbar=q), 0, 5, singular=True)&lt;br /&gt;[1.0, 744.0, 196884.0, 21493760.0, 864299970.0, 20245856256.0]&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;The J-function also looks pretty when plotted, here as a function of the number-theoretic nome within the unit circle:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;fp.cplot(lambda q: fp.kleinj(qbar=q), [-1,1], [-1,1], points=400000)&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_rh0QblLk0C0/TAumYUBATRI/AAAAAAAAAPQ/cZHl7pWpwm0/s1600/kleinj.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 400px; height: 379px;" src="http://3.bp.blogspot.com/_rh0QblLk0C0/TAumYUBATRI/AAAAAAAAAPQ/cZHl7pWpwm0/s400/kleinj.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5479656308246334738" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;As a function of the half-period ratio τ, defined in the upper half-plane:&lt;br /&gt;&lt;pre&gt;fp.cplot(lambda t: fp.kleinj(tau=t), [-1,2], [0,1.5], points=100000)&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_rh0QblLk0C0/TAunSudP8pI/AAAAAAAAAPY/AlGlR3v3Jlk/s1600/kleinj_tau.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 400px; height: 211px;" src="http://3.bp.blogspot.com/_rh0QblLk0C0/TAunSudP8pI/AAAAAAAAAPY/AlGlR3v3Jlk/s400/kleinj_tau.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5479657311776535186" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;h3&gt;Complex interval arithmetic&lt;/h3&gt;&lt;br /&gt;The support for interval arithmetic has been extended; there is also a new interface for intervals. It is no longer possible to call &lt;tt&gt;mpmath.exp&lt;/tt&gt; etc. directly with interval arguments; instead all interval arithmetic has been moved to a separate context object called &lt;tt&gt;iv&lt;/tt&gt; (similar to the separate interface for working with &lt;a href="http://fredrik-j.blogspot.com/2009/09/python-floats-and-other-unusual-things.html"&gt;Python float/complex&lt;/a&gt; in mpmath). For example:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;&gt;&gt;&gt; iv.dps = 5; iv.pretty = True&lt;br /&gt;&gt;&gt;&gt; iv.mpf(1)/3&lt;br /&gt;[0.3333330154, 0.3333334923]&lt;br /&gt;&gt;&gt;&gt; iv.sin(1)&lt;br /&gt;[0.8414707184, 0.8414716721]&lt;br /&gt;&gt;&gt;&gt; iv.sin([1,2])&lt;br /&gt;[0.8414707184, 1.0]&lt;br /&gt;&gt;&gt;&gt; iv.sin([1,3])&lt;br /&gt;[0.141119957, 1.0]&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;The interface change was done to simplify the implementation; separating out interval arithmetic also helps avoid inadvertent mixing of interval and non-interval arithmetic.&lt;br /&gt;&lt;br /&gt;There is also some support for complex interval arithmetic, including some elementary functions (exp, log, cos, sin, power).&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;&gt;&gt;&gt; iv.mpc('1.3','1.4') ** 2&lt;br /&gt;([-0.2700066566, -0.2699956894] + [3.639995575, 3.640010834]*j)&lt;br /&gt;&gt;&gt;&gt; iv.mpc('1.3','1.4') ** iv.mpc('1.2','-0.7')&lt;br /&gt;([3.329280853, 3.329311371] + [1.967472076, 1.967498779]*j)&lt;br /&gt;&gt;&gt;&gt; iv.dps = 25&lt;br /&gt;&gt;&gt;&gt; iv.mpc('1.3','1.4') ** 2&lt;br /&gt;([-0.270000000000000000000000061005, -0.269999999999999999999999912371] +&lt;br /&gt;[3.63999999999999999999999988419, 3.64000000000000000000000009099]*j)&lt;br /&gt;&gt;&gt;&gt; mp.cos(2+3j)&lt;br /&gt;(-4.189625690968807230132555 - 9.109227893755336597979197j)&lt;br /&gt;&gt;&gt;&gt; iv.cos(2+3j)&lt;br /&gt;([-4.18962569096880723013255508975, -4.18962569096880723013255498635] + &lt;br /&gt;[-9.1092278937553365979791973006, -9.10922789375533659797919709381]*j)&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Verifying &lt;a href="http://en.wikipedia.org/wiki/Euler%27s_identity"&gt;Euler's identity&lt;/a&gt;:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;&gt;&gt;&gt; iv.dps = 5&lt;br /&gt;&gt;&gt;&gt; iv.exp(1j * iv.pi)&lt;br /&gt;([-1.0, -0.9999990463] + [-1.279517164e-6, 2.535183739e-6]*j)&lt;br /&gt;&gt;&gt;&gt; iv.dps = 25&lt;br /&gt;&gt;&gt;&gt; iv.exp(1j * iv.pi)&lt;br /&gt;([-1.0, -0.999999999999999999999999987075] +&lt;br /&gt;[-1.8806274411915714063022730148e-26, 3.28925138726485156164403137746e-26]*j)&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Rigorous complex linear algebra:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;&gt;&gt;&gt; iv.dps = 15&lt;br /&gt;&gt;&gt;&gt; A = iv.matrix([[2,-2,1],[1,0,2],[1,1,2]])&lt;br /&gt;&gt;&gt;&gt; b = iv.matrix([1,2+2j,5])&lt;br /&gt;&gt;&gt;&gt; iv.lu_solve(A,b)&lt;br /&gt;[ ([4.0, 4.0] + [-3.3333333333333333339, -3.333333333333333333]*j)]&lt;br /&gt;[([3.0, 3.0] + [-2.0000000000000000004, -1.9999999999999999998]*j)]&lt;br /&gt;[ ([-1.0, -1.0] + [2.6666666666666666665, 2.666666666666666667]*j)]&lt;br /&gt;&gt;&gt;&gt; A*iv.lu_solve(A,b)&lt;br /&gt;[([1.0, 1.0] + [-8.8817841970012523234e-16, 1.7763568394002504647e-15]*j)]&lt;br /&gt;[         ([2.0, 2.0] + [1.9999999999999995559, 2.0000000000000008882]*j)]&lt;br /&gt;[([5.0, 5.0] + [-8.8817841970012523234e-16, 1.7763568394002504647e-15]*j)]&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;h3&gt;Partial derivatives&lt;/h3&gt;&lt;br /&gt;&lt;br /&gt;This is rather simple, but very convenient. &lt;tt&gt;diff&lt;/tt&gt; can now compute partial derivatives of arbitrary order for multivariable functions. For example, a first derivative with respect to the second argument:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;&gt;&gt;&gt; diff(lambda x,y: 3*x*y + 2*y - x, (0.25, 0.5), (0,1))&lt;br /&gt;2.75&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;The first derivative with respect to both &lt;i&gt;x&lt;/i&gt; and &lt;i&gt;y&lt;/i&gt;:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;&gt;&gt;&gt; diff(lambda x,y: 3*x*y + 2*y - x, (0.25, 0.5), (1,1))&lt;br /&gt;3.0&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Second derivative with respect to a parameter of a hypergeometric function &lt;sub&gt;2&lt;/sub&gt;F&lt;sub&gt;1&lt;/sub&gt;(&lt;i&gt;a&lt;/i&gt;,&lt;i&gt;b&lt;/i&gt;,&lt;i&gt;c&lt;/i&gt;,&lt;i&gt;z&lt;/i&gt;); tenth derivative with respect to the argument; first derivative with respect to two parameters and fourth derivative with respect to the argument:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;&gt;&gt;&gt; diff(hyp2f1, (1,2,3,0.5), (0,0,2,0))&lt;br /&gt;0.2306514802159766624657329&lt;br /&gt;&gt;&gt;&gt; diff(hyp2f1, (1,2,3,0.5), (0,0,0,10))&lt;br /&gt;672869392.1810426015397572&lt;br /&gt;&gt;&gt;&gt; diff(hyp2f1, (1,2,3,0.5), (1,0,1,4))&lt;br /&gt;-585.1335939248098118753126&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;That's it for now. Stay tuned for many new features in the near future!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4754734402679928849-234420875625644251?l=fredrik-j.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://fredrik-j.blogspot.com/feeds/234420875625644251/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=4754734402679928849&amp;postID=234420875625644251' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4754734402679928849/posts/default/234420875625644251'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4754734402679928849/posts/default/234420875625644251'/><link rel='alternate' type='text/html' href='http://fredrik-j.blogspot.com/2010/06/announcing-mpmath-015.html' title='Announcing mpmath 0.15'/><author><name>Fredrik Johansson</name><uri>http://www.blogger.com/profile/01465400860530971858</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://3.bp.blogspot.com/_rh0QblLk0C0/TAumYUBATRI/AAAAAAAAAPQ/cZHl7pWpwm0/s72-c/kleinj.png' height='72' width='72'/><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4754734402679928849.post-59187826230880460</id><published>2010-03-17T10:21:00.011+01:00</published><updated>2010-03-17T18:27:38.270+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='sage'/><category scheme='http://www.blogger.com/atom/ns#' term='sympy'/><category scheme='http://www.blogger.com/atom/ns#' term='mpmath'/><title type='text'>Hypergeneralization</title><content type='html'>The &lt;a href="http://mpmath.googlecode.com/svn/trunk/doc/build/functions/hypergeometric.html#generalized-hypergeometric-functions"&gt;generalized hypergeometric function&lt;/a&gt; can itself be generalized endlessly. I have recently implemented three such extensions in mpmath: bilateral series, two-dimensional hypergeometric series, and &lt;i&gt;q&lt;/i&gt;-analog (or "basic") hypergeometric series.&lt;br /&gt;&lt;br /&gt;&lt;h3&gt;Bilateral series&lt;/h3&gt;&lt;br /&gt;The &lt;a href="http://en.wikipedia.org/wiki/Bilateral_hypergeometric_series"&gt;bilateral hypergeometric series&lt;/a&gt; is the simplest extension, and consists of taking the usual hypergeometric series and extending the range of summation from [0,∞) to (-∞,∞):&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_rh0QblLk0C0/S6CftbGPcYI/AAAAAAAAAOw/GsDYbUeiXic/s1600-h/75a6680a08d19b12ed51c10c3f2435c188d4eebe.png"&gt;&lt;img style="display: block; margin: 0px auto 10px; text-align: center; cursor: pointer; width: 400px; height: 47px;" src="http://3.bp.blogspot.com/_rh0QblLk0C0/S6CftbGPcYI/AAAAAAAAAOw/GsDYbUeiXic/s400/75a6680a08d19b12ed51c10c3f2435c188d4eebe.png" alt="" id="BLOGGER_PHOTO_ID_5449531151835165058" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;This series only converges when |&lt;i&gt;z&lt;/i&gt;| = 1 and &lt;i&gt;A&lt;/i&gt; = &lt;i&gt;B&lt;/i&gt;, but interpreted as a sum of two ordinary hypergeometric series, it can be assigned a value for arbitrary &lt;i&gt;z&lt;/i&gt; through the analytic continuation (or Borel regularization) of the ordinary hypergeometric series, which mpmath implements. Anyway, the convergent case is the interesting one. Here one obtains, for instance, &lt;a href="http://mathworld.wolfram.com/DougallsFormula.html"&gt;Dougall's identity&lt;/a&gt; for 2H2:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;&gt;&gt;&gt; from mpmath import *&lt;br /&gt;&gt;&gt;&gt; mp.dps = 25; mp.pretty = True&lt;br /&gt;&gt;&gt;&gt; a,b,c,d = 0.5, 1.5, 2.25, 3.25&lt;br /&gt;&gt;&gt;&gt; bihyper([a,b],[c,d],1)&lt;br /&gt;-14.49118026212345786148847&lt;br /&gt;&gt;&gt;&gt; gammaprod([c,d,1-a,1-b,c+d-a-b-1],[c-a,d-a,c-b,d-b])&lt;br /&gt;-14.49118026212345786148847&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;As an example of regularization, the divergent 1H0 series can be expressed as the sum of one 2F0 function and one 1F1 function:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;&gt;&gt;&gt; a = mpf(0.25)&lt;br /&gt;&gt;&gt;&gt; z = mpf(0.75)&lt;br /&gt;&gt;&gt;&gt; bihyper([a], [], z)&lt;br /&gt;(0.2454393389657273841385582 + 0.2454393389657273841385582j)&lt;br /&gt;&gt;&gt;&gt; hyper([a,1],[],z) + (hyper([1],[1-a],-1/z)-1)&lt;br /&gt;(0.2454393389657273841385582 + 0.2454393389657273841385582j)&lt;br /&gt;&gt;&gt;&gt; hyper([a,1],[],z) + hyper([1],[2-a],-1/z)/z/(a-1)&lt;br /&gt;(0.2454393389657273841385582 + 0.2454393389657273841385582j)&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;h3&gt;Two-dimensional series&lt;/h3&gt;&lt;br /&gt;The most common hypergeometric series of two variables, i.e. a twodimensional series whose summand is a hypergeometric expression with respect to both indices separately, is the Appell F1 function, previously available in mpmath as &lt;tt&gt;appellf1&lt;/tt&gt;:&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://1.bp.blogspot.com/_rh0QblLk0C0/S6Cj4Gzq-gI/AAAAAAAAAO4/FtgLLpKSFTk/s1600-h/2a732d5aa33892d5768d4c5ab48008ce0ce48ba3.png"&gt;&lt;img style="display: block; margin: 0px auto 10px; text-align: center; cursor: pointer; width: 400px; height: 50px;" src="http://1.bp.blogspot.com/_rh0QblLk0C0/S6Cj4Gzq-gI/AAAAAAAAAO4/FtgLLpKSFTk/s400/2a732d5aa33892d5768d4c5ab48008ce0ce48ba3.png" alt="" id="BLOGGER_PHOTO_ID_5449535733413640706" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;However, much more general functions are possible. There are three other Appell functions: F2, F3, F4. The &lt;a href="http://mathworld.wolfram.com/HornFunction.html"&gt;Horn functions&lt;/a&gt; are 34 distinct functions of order two, containing the Appell functions as special cases. The &lt;a href="http://mathworld.wolfram.com/KampedeFerietFunction.html"&gt;Kampé de Fériet function&lt;/a&gt; provides a generalization of Appell functions to arbitrary orders.&lt;br /&gt;&lt;br /&gt;The new &lt;tt&gt;hyper2d&lt;/tt&gt; function in mpmath can evaluate &lt;b&gt;all&lt;/b&gt; these named functions, and more general functions still. The trick for speed is to write the series as a nested series, where the inner series is a generalized hypergeometric series that can be evaluated efficiently with &lt;tt&gt;hyper&lt;/tt&gt;, and where the outer series has a rational recurrence formula. This rewriting also permits evaluating the analytic continuation with respect to the inner variable (as implemented by &lt;tt&gt;hyper&lt;/tt&gt;).&lt;br /&gt;&lt;br /&gt;The user specifies the format of the series in quasi-symbolic form, and the rewriting to nested form is done automatically by mpmath. For example, the Appell F1 function can be computed as&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;hyper2d({'m+n':[a], 'm':[b1], 'n':[b2]}, {'m+n':[c]}, x, y)&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;and indeed, this is essentially what &lt;tt&gt;appellf1&lt;/tt&gt; now does internally. The Appell F2-F4 functions have also been added explicitly as &lt;tt&gt;appellf2&lt;/tt&gt;, &lt;tt&gt;appellf3&lt;/tt&gt;, &lt;tt&gt;appellf4&lt;/tt&gt;.&lt;br /&gt;&lt;br /&gt;Hypergeometric functions of two (or more) variables have numerous applications, such as solving high-order algebraic equations, expressing various derivatives and integrals in closed form, and solving differential equations, but I have not yet found any simple examples that make good demonstrations except for F1. (I have mostly found examples of that take half a page to write down.) Any such examples for the documentation would be a welcome contribution!&lt;br /&gt;&lt;br /&gt;Some trivial examples from the documentation are:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;&gt;&gt;&gt; x, y = mpf(0.25), mpf(0.5)&lt;br /&gt;&gt;&gt;&gt; hyper2d({'m':1,'n':1}, {}, x,y)&lt;br /&gt;2.666666666666666666666667&lt;br /&gt;&gt;&gt;&gt; 1/(1-x)/(1-y)&lt;br /&gt;2.666666666666666666666667&lt;br /&gt;&gt;&gt;&gt; hyper2d({'m':[1,2],'n':[3,4]}, {'m':[5],'n':[6]}, x,y)&lt;br /&gt;4.164358531238938319669856&lt;br /&gt;&gt;&gt;&gt; hyp2f1(1,2,5,x)*hyp2f1(3,4,6,y)&lt;br /&gt;4.164358531238938319669856&lt;br /&gt;&gt;&gt;&gt; hyper2d({'m':1,'n':1},{'m+n':1},x,y)&lt;br /&gt;2.013417124712514809623881&lt;br /&gt;&gt;&gt;&gt; (exp(x)*x-exp(y)*y)/(x-y)&lt;br /&gt;2.013417124712514809623881&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;An example of a Horn function, H3:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;&gt;&gt;&gt; x, y = 0.0625, 0.125&lt;br /&gt;&gt;&gt;&gt; a,b,c = 0.5,0.75,0.625&lt;br /&gt;&gt;&gt;&gt; hyper2d({'2m+n':a,'n':b},{'m+n':c},x,y)&lt;br /&gt;1.190003093972956004227425&lt;br /&gt;&gt;&gt;&gt; nsum(lambda m,n: rf(a,2*m+n)*rf(b,n)/rf(c,m+n)*\&lt;br /&gt;...     x**m*y**n/fac(m)/fac(n), [0,inf], [0,inf])&lt;br /&gt;1.190003093972956004227425&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;This also demonstrates the recently added generic support for &lt;a href="http://fredrik-j.blogspot.com/2010/02/numerical-multidimensional-infinite.html"&gt;multidimensional infinite series&lt;/a&gt; in mpmath. But of course, &lt;tt&gt;nsum&lt;/tt&gt; is much slower than &lt;tt&gt;hyper2d&lt;/tt&gt;.&lt;br /&gt;&lt;br /&gt;&lt;h3&gt;Hypergeometric &lt;i&gt;q&lt;/i&gt;-series&lt;/h3&gt;&lt;br /&gt;Before introducing the &lt;i&gt;q&lt;/i&gt;-analog of the hypergeometric series, I should introduce the &lt;a href="http://en.wikipedia.org/wiki/Q-Pochhammer_symbol"&gt;&lt;i&gt;q&lt;/i&gt;-Pochhammer symbol&lt;/a&gt;,&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://2.bp.blogspot.com/_rh0QblLk0C0/S6CzB6_YYbI/AAAAAAAAAPA/xEmIbtVz-7c/s1600-h/qpoch.png"&gt;&lt;img style="display: block; margin: 0px auto 10px; text-align: center; cursor: pointer; width: 165px; height: 53px;" src="http://2.bp.blogspot.com/_rh0QblLk0C0/S6CzB6_YYbI/AAAAAAAAAPA/xEmIbtVz-7c/s400/qpoch.png" alt="" id="BLOGGER_PHOTO_ID_5449552394714636722" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;This itself is a new function in mpmath, implemented as &lt;tt&gt;qp(a,q,n)&lt;/tt&gt; (with two- and one-argument forms &lt;tt&gt;qp(a,q)&lt;/tt&gt; and &lt;tt&gt;qp(q)&lt;/tt&gt; also permitted) and is the basis for more general computation involving &lt;a href="http://en.wikipedia.org/wiki/Q-analog"&gt;q-analogs&lt;/a&gt;. The &lt;i&gt;q&lt;/i&gt;-factorial and &lt;i&gt;q&lt;/i&gt;-gamma function have also been added (as &lt;tt&gt;qfac&lt;/tt&gt; and &lt;tt&gt;qgamma&lt;/tt&gt;), but are not yet documented.&lt;br /&gt;&lt;br /&gt;The &lt;i&gt;q&lt;/i&gt;-analogs have important applications in number theory. As a very neat example, numerically computing the Taylor series of 1/(&lt;i&gt;q&lt;/i&gt;, &lt;i&gt;q&lt;/i&gt;)&lt;sub&gt;∞&lt;/sub&gt; with mpmath gives&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;&gt;&gt;&gt; taylor(lambda q: 1/qp(q), 0, 10)&lt;br /&gt;[1.0, 1.0, 2.0, 3.0, 5.0, 7.0, 11.0, 15.0, 22.0, 30.0, 42.0]&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;These are the values of the &lt;a href="http://mathworld.wolfram.com/PartitionFunctionP.html"&gt;partition function&lt;/a&gt; &lt;i&gt;P&lt;/i&gt;(&lt;i&gt;n&lt;/i&gt;) for &lt;i&gt;n&lt;/i&gt; = 0,1,2,..., i.e. the number of ways of writing &lt;i&gt;n&lt;/i&gt; as a sum of positive integers.&lt;br /&gt;&lt;br /&gt;Replacing the rising factorials (Pochhammer symbols) in the generalized hypergeometric series with their &lt;i&gt;q&lt;/i&gt;-analogs gives the hypergeometric &lt;i&gt;q&lt;/i&gt;-series or &lt;i&gt;&lt;a href="http://en.wikipedia.org/wiki/Basic_hypergeometric_series"&gt;basic hypergeometric series&lt;/a&gt;&lt;/i&gt;&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_rh0QblLk0C0/S6C2H65uubI/AAAAAAAAAPI/IciuynXeI70/s1600-h/a8efaffc0e5fe799f97388cdc4eef302ba675f3f.png"&gt;&lt;img style="display: block; margin: 0px auto 10px; text-align: center; cursor: pointer; width: 400px; height: 34px;" src="http://4.bp.blogspot.com/_rh0QblLk0C0/S6C2H65uubI/AAAAAAAAAPI/IciuynXeI70/s400/a8efaffc0e5fe799f97388cdc4eef302ba675f3f.png" alt="" id="BLOGGER_PHOTO_ID_5449555796305033650" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;This function is implemented as &lt;tt&gt;qhyper&lt;/tt&gt;. Like &lt;tt&gt;hyper&lt;/tt&gt;, it supports arbitrary combinations of real and complex arguments (assuming |&lt;i&gt;q&lt;/i&gt;| &lt; 1). Some examples from the documentation:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;&gt;&gt;&gt; qhyper([0.5], [2.25], 0.25, 4)&lt;br /&gt;-0.1975849091263356009534385&lt;br /&gt;&gt;&gt;&gt; qhyper([0.5], [2.25], 0.25-0.25j, 4)&lt;br /&gt;(2.806330244925716649839237 + 3.568997623337943121769938j)&lt;br /&gt;&gt;&gt;&gt; qhyper([1+j], [2,3+0.5j], 0.25, 3+4j)&lt;br /&gt;(9.112885171773400017270226 - 1.272756997166375050700388j)&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Like &lt;tt&gt;hyper&lt;/tt&gt;, it automatically ensures accurate evaluation for alternating series:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;&gt;&gt;&gt; q = 0.998046875&lt;br /&gt;&gt;&gt;&gt; mp.dps=5; qhyper([2],[0.5], q, -0.5)&lt;br /&gt;6.6738e-69&lt;br /&gt;&gt;&gt;&gt; mp.dps=15; qhyper([2],[0.5], q, -0.5)&lt;br /&gt;6.67376764851253e-69&lt;br /&gt;&gt;&gt;&gt; mp.dps=25; qhyper([2],[0.5], q, -0.5)&lt;br /&gt;6.673767648512527695718826e-69&lt;br /&gt;&gt;&gt;&gt; mp.dps=100; qhyper([2],[0.5], q, -0.5)&lt;br /&gt;6.673767648512527695718826106778799352769798151218768443717704076836963752188876&lt;br /&gt;561888441662933081804e-69&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;With the &lt;i&gt;q&lt;/i&gt;-analog of the generalized hypergeometric function implemented, it becomes possible to compute &lt;i&gt;q&lt;/i&gt;-exponentials, &lt;i&gt;q&lt;/i&gt;-sines, &lt;i&gt;q&lt;/i&gt;-orthogonal polynomials, &lt;i&gt;q&lt;/i&gt;-Bessel functions, and pretty much anything else. If there is interest, such function could be added explicitly to mpmath.&lt;br /&gt;&lt;br /&gt;For more information and examples of the functions discussed in this post, see the sections on &lt;a href="http://mpmath.googlecode.com/svn/trunk/doc/build/functions/hypergeometric.html"&gt;hypergeometric functions&lt;/a&gt; and &lt;a href="http://mpmath.googlecode.com/svn/trunk/doc/build/functions/qfunctions.html"&gt;&lt;i&gt;q&lt;/i&gt;-functions&lt;/a&gt; (a little terse at the moment) in the mpmath documentation.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4754734402679928849-59187826230880460?l=fredrik-j.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://fredrik-j.blogspot.com/feeds/59187826230880460/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=4754734402679928849&amp;postID=59187826230880460' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4754734402679928849/posts/default/59187826230880460'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4754734402679928849/posts/default/59187826230880460'/><link rel='alternate' type='text/html' href='http://fredrik-j.blogspot.com/2010/03/hypergeneralization.html' title='Hypergeneralization'/><author><name>Fredrik Johansson</name><uri>http://www.blogger.com/profile/01465400860530971858</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://3.bp.blogspot.com/_rh0QblLk0C0/S6CftbGPcYI/AAAAAAAAAOw/GsDYbUeiXic/s72-c/75a6680a08d19b12ed51c10c3f2435c188d4eebe.png' height='72' width='72'/><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4754734402679928849.post-47402428894913812</id><published>2010-03-12T14:04:00.006+01:00</published><updated>2010-03-12T17:57:32.183+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='sage'/><category scheme='http://www.blogger.com/atom/ns#' term='sympy'/><category scheme='http://www.blogger.com/atom/ns#' term='mpmath'/><title type='text'>Computing large zeta zeros with mpmath</title><content type='html'>&lt;a href="http://personal.us.es/arias/"&gt;Juan Arias de Reyna&lt;/a&gt;, who wrote the code used in mpmath for evaluating the Riemann zeta function near the critical strip, has &lt;a href="http://code.google.com/p/mpmath/source/detail?r=1096"&gt;contributed&lt;/a&gt; a new implementation of the &lt;tt&gt;zetazero&lt;/tt&gt; function which computes the &lt;i&gt;n&lt;/i&gt;th zero on the critical line.&lt;br /&gt;&lt;br /&gt;The old version (written by myself) used a lookup table for initial approximations, so it was limited to computing the first few zeros. Juan's code calculates the position of arbitrary zeros using &lt;a href="http://mathworld.wolfram.com/GramsLaw.html"&gt;Gram's law&lt;/a&gt;, with a sophisticated algorithm (about 300 lines of code) to find the correct zero when Gram's law (which is actually just a heuristic) fails.&lt;br /&gt;&lt;br /&gt;A bunch of zeros, from small to large:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;from mpmath import *&lt;br /&gt;from timeit import default_timer as clock&lt;br /&gt;mp.dps = 20&lt;br /&gt;for n in range(14):&lt;br /&gt;    t1 = clock()&lt;br /&gt;    v1 = zetazero(10**n)&lt;br /&gt;    t2 = clock()&lt;br /&gt;    v2 = zetazero(10**n+1)&lt;br /&gt;    t3 = clock()&lt;br /&gt;    print "10&amp;lt;sup&gt;%i&amp;lt;/sup&gt;  "%n, v1, "%.2f" % (t2-t1)&lt;br /&gt;    print "10&amp;lt;sup&gt;%i&amp;lt;/sup&gt;+1"%n, v2, "%.2f" % (t3-t2)&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;n      value                          time (s)&lt;br /&gt;10&lt;sup&gt;0&lt;/sup&gt;    (0.5 + 14.13472514173469379j)  0.12&lt;br /&gt;10&lt;sup&gt;0&lt;/sup&gt;+1  (0.5 + 21.022039638771554993j) 0.05&lt;br /&gt;10&lt;sup&gt;1&lt;/sup&gt;    (0.5 + 49.773832477672302182j) 0.05&lt;br /&gt;10&lt;sup&gt;1&lt;/sup&gt;+1  (0.5 + 52.970321477714460644j) 0.04&lt;br /&gt;10&lt;sup&gt;2&lt;/sup&gt;    (0.5 + 236.5242296658162058j)  0.32&lt;br /&gt;10&lt;sup&gt;2&lt;/sup&gt;+1  (0.5 + 237.769820480925204j)   0.30&lt;br /&gt;10&lt;sup&gt;3&lt;/sup&gt;    (0.5 + 1419.4224809459956865j) 0.93&lt;br /&gt;10&lt;sup&gt;3&lt;/sup&gt;+1  (0.5 + 1420.416526323751136j)  0.84&lt;br /&gt;10&lt;sup&gt;4&lt;/sup&gt;    (0.5 + 9877.7826540055011428j) 3.75&lt;br /&gt;10&lt;sup&gt;4&lt;/sup&gt;+1  (0.5 + 9878.6547723856922882j) 3.71&lt;br /&gt;10&lt;sup&gt;5&lt;/sup&gt;    (0.5 + 74920.827498994186794j) 2.53&lt;br /&gt;10&lt;sup&gt;5&lt;/sup&gt;+1  (0.5 + 74921.929793958414308j) 2.58&lt;br /&gt;10&lt;sup&gt;6&lt;/sup&gt;    (0.5 + 600269.67701244495552j) 2.43&lt;br /&gt;10&lt;sup&gt;6&lt;/sup&gt;+1  (0.5 + 600270.30109071169866j) 2.89&lt;br /&gt;10&lt;sup&gt;7&lt;/sup&gt;    (0.5 + 4992381.014003178666j)  1.63&lt;br /&gt;10&lt;sup&gt;7&lt;/sup&gt;+1  (0.5 + 4992381.2627112065366j) 2.30&lt;br /&gt;10&lt;sup&gt;8&lt;/sup&gt;    (0.5 + 42653549.760951553903j) 2.36&lt;br /&gt;10&lt;sup&gt;8&lt;/sup&gt;+1  (0.5 + 42653550.046758478876j) 2.93&lt;br /&gt;10&lt;sup&gt;9&lt;/sup&gt;    (0.5 + 371870203.83702805273j) 6.98&lt;br /&gt;10&lt;sup&gt;9&lt;/sup&gt;+1  (0.5 + 371870204.36631304458j) 6.72&lt;br /&gt;10&lt;sup&gt;10&lt;/sup&gt;   (0.5 + 3293531632.3971367042j) 11.48&lt;br /&gt;10&lt;sup&gt;10&lt;/sup&gt;+1 (0.5 + 3293531632.6869557853j) 15.92&lt;br /&gt;10&lt;sup&gt;11&lt;/sup&gt;   (0.5 + 29538618431.613072811j) 53.67&lt;br /&gt;10&lt;sup&gt;11&lt;/sup&gt;+1 (0.5 + 29538618432.07777426j)  43.00&lt;br /&gt;10&lt;sup&gt;12&lt;/sup&gt;   (0.5 + 267653395648.62594824j) 162.01&lt;br /&gt;10&lt;sup&gt;12&lt;/sup&gt;+1 (0.5 + 267653395648.84752313j) 174.67&lt;br /&gt;10&lt;sup&gt;13&lt;/sup&gt;   (0.5 + 2445999556030.2468814j) 1262.55&lt;br /&gt;10&lt;sup&gt;13&lt;/sup&gt;+1 (0.5 + 2445999556030.6222451j) 1456.38&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;The function correctly separates close zeros, and can optionally return information about the separation. For example:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;&gt;&gt;&gt; mp.dps = 20; mp.pretty = True&lt;br /&gt;&gt;&gt;&gt; zetazero(542964976,info=True)&lt;br /&gt;((0.5 + 209039046.578535272j), [542964969, 542964978], 6, '(013111110)')&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;The extra information is explained in the docstring for &lt;tt&gt;zetazero&lt;/tt&gt;:&lt;br /&gt;&lt;blockquote&gt;&lt;br /&gt;[This means that the] zero is between Gram points 542964969 and 542964978, it is the 6-th zero between them. Finally (01311110) is the pattern of zeros in this interval. The numbers indicate the number of zeros in each Gram interval, (Rosser Blocks between parenthesis). In this case only one Rosser Block of length nine.&lt;br /&gt;&lt;/blockquote&gt;&lt;br /&gt;&lt;br /&gt;Juan reports having computed the 10&lt;sup&gt;15&lt;/sup&gt;th zero, which is&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;208514052006405.46942460229754774510611&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;and verifying that it satisfies the Riemann hypothesis. And fortunately, the values of large zeros computed with mpmath seem to agree with those reported by &lt;a href="http://www.dtc.umn.edu/~odlyzko/doc/zeta.html"&gt;Andrew Odlyzko&lt;/a&gt; and &lt;a href="http://numbers.computation.free.fr/Constants/Miscellaneous/zeta.html"&gt;Xavier Gourdon&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;The new &lt;tt&gt;zetazero&lt;/tt&gt; function in mpmath is indeed able to separate the closest known pair of zeros, found in Xavier Gourdon's exhaustive search up to 10&lt;sup&gt;13&lt;/sup&gt;:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;&gt;&gt;&gt; mp.dps = 25&lt;br /&gt;&gt;&gt;&gt; v1 = zetazero(8637740722917, info=True)&lt;br /&gt;&gt;&gt;&gt; v2 = zetazero(8637740722918, info=True)&lt;br /&gt;&gt;&gt;&gt; v1&lt;br /&gt;((0.5 + 2124447368584.392964661515j), [8637740722909L, 8637740722925L], 7L,&lt;br /&gt;'(1)(1)(1)(1)(1)(1)(02)(1)(02)(1)(1)(20)(1)')&lt;br /&gt;&gt;&gt;&gt; v2&lt;br /&gt;((0.5 + 2124447368584.392981706039j), [8637740722909L, 8637740722925L], 8L,&lt;br /&gt;'(1)(1)(1)(1)(1)(1)(02)(1)(02)(1)(1)(20)(1)')&lt;br /&gt;&gt;&gt;&gt; nprint(abs(v1[0]-v2[0]))&lt;br /&gt;1.70445e-5&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;This computation takes over 2 hours on my laptop.&lt;br /&gt;&lt;br /&gt;Of course, zeros can be computed to any desired precision:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;&gt;&gt;&gt; mp.dps = 1000&lt;br /&gt;&gt;&gt;&gt; print zetazero(1)&lt;br /&gt;(0.5 + 14.1347251417346937904572519835624702707842571156992431756855674601499634&lt;br /&gt;29809256764949010393171561012779202971548797436766142691469882254582505363239447&lt;br /&gt;13778041338123720597054962195586586020055556672583601077370020541098266150754278&lt;br /&gt;05174425913062544819786510723049387256297383215774203952157256748093321400349904&lt;br /&gt;68034346267314420920377385487141378317356396995365428113079680531491688529067820&lt;br /&gt;82298049264338666734623320078758761792005604868054356801444424651065597568665903&lt;br /&gt;22868651054485944432062407272703209427452221304874872092412385141835146054279015&lt;br /&gt;24478338354254533440044879368067616973008190007313938549837362150130451672696838&lt;br /&gt;92003917628512321285422052396913342583227533516406016976352756375896953767492033&lt;br /&gt;61272092599917304270756830879511844534891800863008264831251691127106829105237596&lt;br /&gt;17977431815170713545316775495153828937849036474709727019948485532209253574357909&lt;br /&gt;22612524773659551801697523346121397731600535412592674745572587780147260983080897&lt;br /&gt;860071253208750939599796666067537838121489190886j)&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;The only real limitation of the code, as far as I can tell, is that it's impractical for computing a large number of consecutive zeros. For that one needs to use something like the &lt;a href="http://en.wikipedia.org/wiki/Odlyzko%E2%80%93Sch%C3%B6nhage_algorithm"&gt;Odlyzko–Schönhage algorithm&lt;/a&gt; for multi-evaluation of the zeta function.&lt;br /&gt;&lt;br /&gt;A nice exercise would be to parallelize the zeta code using &lt;a href="http://docs.python.org/library/multiprocessing.html"&gt;multiprocessing&lt;/a&gt;. The speedup for large zeros should be essentially linear with the number of processors.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4754734402679928849-47402428894913812?l=fredrik-j.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://fredrik-j.blogspot.com/feeds/47402428894913812/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=4754734402679928849&amp;postID=47402428894913812' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4754734402679928849/posts/default/47402428894913812'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4754734402679928849/posts/default/47402428894913812'/><link rel='alternate' type='text/html' href='http://fredrik-j.blogspot.com/2010/03/computing-large-zeta-zeros-with-mpmath.html' title='Computing large zeta zeros with mpmath'/><author><name>Fredrik Johansson</name><uri>http://www.blogger.com/profile/01465400860530971858</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4754734402679928849.post-7024271353539884591</id><published>2010-03-11T23:26:00.005+01:00</published><updated>2010-03-12T00:21:06.322+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='sage'/><category scheme='http://www.blogger.com/atom/ns#' term='sympy'/><category scheme='http://www.blogger.com/atom/ns#' term='mpmath'/><title type='text'>Speedups of elementary functions</title><content type='html'>I have a fairly large backlog of changes in mpmath to blog about. For today, I'll just briefly mention the most recent one, which is a set of optimizations to elementary functions. The &lt;a href="http://code.google.com/p/mpmath/source/detail?r=1102"&gt;commit is here&lt;/a&gt;. I intend to provide much faster Cython versions of the elementary functions some time in the future, but since there was room for optimizing the Python versions, I decided to invest a little time in that.&lt;br /&gt;&lt;br /&gt;Most importantly, the asymptotic performance of trigonometric function has been improved greatly, due to a more optimized complexity reduction strategy. The faster strategy was previously used only for exp, but cos/sin are virtually identical in theory so fixing this was mostly a coding problem. In fact, the high-precision code for exp/cosh/sinh/cos/sin is largely shared now, which is nice because only one implementation needs to be optimized.&lt;br /&gt;&lt;br /&gt;The following image shows performance for computing cos(3.7). The red graph is the old implementation; blue is new. The top subplot shows evaluations/second at low precision, i.e. higher is better, and the bottom subplot shows seconds/evaluation at higher precision, i.e. lower is better.&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_rh0QblLk0C0/S5lufa-HhXI/AAAAAAAAAOg/MDYR1rD0i6A/s1600-h/timings_cos.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 400px; height: 302px;" src="http://4.bp.blogspot.com/_rh0QblLk0C0/S5lufa-HhXI/AAAAAAAAAOg/MDYR1rD0i6A/s400/timings_cos.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5447506710376187250" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Already at a few thousand bits, the new code is more than twice as fast, and then it just gets better and better. There is also a little less overhead at low precision now, so the new code is uniformly faster.&lt;br /&gt;&lt;br /&gt;Also exp, cosh and sinh have been optimized slightly. Most importantly, there is a little less overhead at low precision, but asymptotic speed has also improved a bit.&lt;br /&gt;&lt;br /&gt;Performance for computing cosh(3.7):&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_rh0QblLk0C0/S5luc4fIAAI/AAAAAAAAAOY/-Sy6qjSHvyk/s1600-h/timings_cosh.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 400px; height: 302px;" src="http://3.bp.blogspot.com/_rh0QblLk0C0/S5luc4fIAAI/AAAAAAAAAOY/-Sy6qjSHvyk/s400/timings_cosh.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5447506666759651330" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Performance for computing exp(3.7):&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_rh0QblLk0C0/S5luXXPzjHI/AAAAAAAAAOQ/K8cXtIPShrc/s1600-h/timings_exp.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 400px; height: 302px;" src="http://4.bp.blogspot.com/_rh0QblLk0C0/S5luXXPzjHI/AAAAAAAAAOQ/K8cXtIPShrc/s400/timings_exp.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5447506571937680498" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;I should note that the performance depends on some tuning parameters which naturally are system-specific. The results above are on a 32-bit system with gmpy as the backend. I hope to later implement optimized tuning parameters for other systems as well.&lt;br /&gt;&lt;br /&gt;Elementary function performance is of course important globally, but it's especially important for some specific functions. For example, the speed of the Riemann zeta function depends almost proportionally on the total speed of evaluating one exp, one log, one sine, and one cosine, since the main part of the work consists of summing the truncated L-series&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_rh0QblLk0C0/S5l5QUOXBFI/AAAAAAAAAOo/aRzM2kxS9V8/s1600-h/142d10760fbe6a4c6e3452cb51f16b0b.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 400px; height: 49px;" src="http://4.bp.blogspot.com/_rh0QblLk0C0/S5l5QUOXBFI/AAAAAAAAAOo/aRzM2kxS9V8/s400/142d10760fbe6a4c6e3452cb51f16b0b.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5447518545495131218" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;With the improved elementary functions, and some overhead removals to the zetasum code in the same commit, the Riemann zeta function is up to 2x faster now, and about 50% faster on the critical line.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4754734402679928849-7024271353539884591?l=fredrik-j.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://fredrik-j.blogspot.com/feeds/7024271353539884591/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=4754734402679928849&amp;postID=7024271353539884591' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4754734402679928849/posts/default/7024271353539884591'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4754734402679928849/posts/default/7024271353539884591'/><link rel='alternate' type='text/html' href='http://fredrik-j.blogspot.com/2010/03/speedups-of-elementary-functions.html' title='Speedups of elementary functions'/><author><name>Fredrik Johansson</name><uri>http://www.blogger.com/profile/01465400860530971858</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://4.bp.blogspot.com/_rh0QblLk0C0/S5lufa-HhXI/AAAAAAAAAOg/MDYR1rD0i6A/s72-c/timings_cos.png' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4754734402679928849.post-3363514911104419259</id><published>2010-02-12T17:50:00.006+01:00</published><updated>2010-02-12T18:31:42.341+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='sage'/><category scheme='http://www.blogger.com/atom/ns#' term='sympy'/><category scheme='http://www.blogger.com/atom/ns#' term='mpmath'/><title type='text'>Numerical multidimensional infinite series</title><content type='html'>Another new feature in mpmath: &lt;a href="http://code.google.com/p/mpmath/source/detail?r=1090"&gt;support&lt;/a&gt; for multidimensional series in &lt;tt&gt;nsum&lt;/tt&gt;.  Some very simple examples (finite and infinite summations can be combined in any desired order):&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;&gt;&gt;&gt; nsum(lambda i,j: 2**(-i-j), [0,inf], [0,inf])&lt;br /&gt;4.0&lt;br /&gt;&gt;&gt;&gt; nsum(lambda i,j,k: 2**(-i-j-k), [0,inf], [0,inf], [0,inf])&lt;br /&gt;8.0&lt;br /&gt;&gt;&gt;&gt; nsum(lambda i,j,k,l: i/2**j*(i+l)/2**k, [1,2], [0,inf], [1,inf], [2,3])&lt;br /&gt;50.0&lt;br /&gt;&gt;&gt;&gt; nsum((lambda i,j,k,l: 1/(2**(i**2+j**2+k**2+l**2))), *([[-inf,inf]]*4))&lt;br /&gt;20.5423960756379&lt;br /&gt;&gt;&gt;&gt; jtheta(3,0,0.5)**4&lt;br /&gt;20.5423960756379&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;One could of course also make nested calls to &lt;tt&gt;nsum&lt;/tt&gt;, but having a direct syntax is much more convenient. Nested evaluation is also usually inefficient for convergence acceleration, so this is not what &lt;tt&gt;nsum&lt;/tt&gt; does internally. Instead, it combines all the infinite summations to a single summation over growing hypercubes. The distinction is very important for conditionally convergent series.&lt;br /&gt;&lt;br /&gt;For example, &lt;tt&gt;nsum&lt;/tt&gt; can now directly evaluate the &lt;a href="http://mathworld.wolfram.com/MadelungConstants.html"&gt;Madelung constant&lt;/a&gt; in three dimensions (ignore=True is to ignore the singular term at the origin):&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;&gt;&gt;&gt; nsum(lambda i,j,k: (-1)**(i+j+k)/(i**2+j**2+k**2)**0.5,&lt;br /&gt;...     [-inf,inf], [-inf,inf], [-inf,inf], ignore=True)&lt;br /&gt;-1.74756459463318&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;While this evaluation takes several seconds, it is somewhat remarkable that a precise value can be obtained at all. A better way to compute the Madelung constant is using the following rapidly convergent 2D series:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;&gt;&gt;&gt; f = lambda i,j: -12*pi*sech(0.5*pi*sqrt((2*i+1)**2+(2*j+1)**2))**2&lt;br /&gt;&gt;&gt;&gt; nsum(f, [0,inf], [0,inf])&lt;br /&gt;-1.74756459463318&lt;br /&gt;&gt;&gt;&gt; mp.dps = 100&lt;br /&gt;&gt;&gt;&gt; nsum(f, [0,inf], [0,inf])&lt;br /&gt;-1.74756459463318219063621203554439740348516143662474175815282535076504062353276&lt;br /&gt;117989075836269460789&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Another nice application is to evaluate &lt;a href="http://en.wikipedia.org/wiki/Eisenstein_series"&gt;Eisenstein series&lt;/a&gt; directly from the definition:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;&gt;&gt;&gt; tau = 1j&lt;br /&gt;&gt;&gt;&gt; q = qfrom(tau=tau)&lt;br /&gt;&gt;&gt;&gt; nsum(lambda m,n: (m+n*tau)**(-4), [-inf,inf], [-inf,inf], ignore=True)&lt;br /&gt;(3.1512120021539 + 0.0j)&lt;br /&gt;&gt;&gt;&gt; 0.5*sum(jtheta(n,0,q)**8 for n in [2,3,4])*(2*zeta(4))&lt;br /&gt;(3.1512120021539 + 0.0j)&lt;br /&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4754734402679928849-3363514911104419259?l=fredrik-j.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://fredrik-j.blogspot.com/feeds/3363514911104419259/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=4754734402679928849&amp;postID=3363514911104419259' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4754734402679928849/posts/default/3363514911104419259'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4754734402679928849/posts/default/3363514911104419259'/><link rel='alternate' type='text/html' href='http://fredrik-j.blogspot.com/2010/02/numerical-multidimensional-infinite.html' title='Numerical multidimensional infinite series'/><author><name>Fredrik Johansson</name><uri>http://www.blogger.com/profile/01465400860530971858</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4754734402679928849.post-1865918379051735323</id><published>2010-02-11T23:09:00.014+01:00</published><updated>2010-02-16T20:39:43.978+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='sage'/><category scheme='http://www.blogger.com/atom/ns#' term='sympy'/><category scheme='http://www.blogger.com/atom/ns#' term='mpmath'/><title type='text'>A new gamma function implementation</title><content type='html'>The gamma function is probably the most important nonelementary special function. The gamma function or ratios of gamma functions appear in everything from the functional equation of the Riemann zeta function to the normalization factors of hypergeometric functions. Even expressions that are usually rational functions (such as binomial coefficients) are often most conveniently expressed (or in software, implemented) in terms of the gamma function since this automatically provides correct asymptotics and extension to noninteger parameters. Needless to say, a well-implemented gamma function is fundamental for a special functions library.&lt;br /&gt;&lt;br /&gt;The gamma function implementation used in mpmath until now is one of the oldest parts of the code (I wrote it around three years ago), and although it has done its job, it has some flaws. It mainly uses &lt;a href="http://en.wikipedia.org/wiki/Spouge%27s_approximation"&gt;Spouge's approximation&lt;/a&gt;, which is fairly efficient but nonoptimal for asymptotically large arguments. The implementation also has some subtle precision issues.&lt;br /&gt;&lt;br /&gt;The new gamma function added in &lt;a href="http://code.google.com/p/mpmath/source/detail?r=1089"&gt;this commit&lt;/a&gt; is the result of over a year of intermittent work. I did most of the work during last summer but didn't have time to finish it until recently. The improvements are essentially the following:&lt;br /&gt;&lt;ul&gt;&lt;br /&gt;&lt;li&gt;Removal of various overheads&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Special-casing half-integers&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Using Taylor series for small real arguments&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Using Stirling's series for complex and/or large arguments&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Optimizing for computing &lt;tt&gt;loggamma&lt;/tt&gt;&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Handling values near poles and near the real axis extremely carefully&lt;/li&gt;&lt;br /&gt;&lt;/ul&gt;&lt;br /&gt;The difference in performance is quite dramatic. The smallest speedup occurs for small complex arguments, but it turns out that even there, the Stirling series is faster than Spouge's approximation (at least with the removal of overheads in the new implementation) despite the need to perform argument transformations. For real arguments, and large complex arguments, the new implementation is consistently faster by a large factor, especially for &lt;tt&gt;loggamma&lt;/tt&gt;.&lt;br /&gt;&lt;br /&gt;I'll show some benchmarks. The times are in milliseconds; the first value in each cell is the time for the old implementation and the second is the time for the new one, followed by the speedup. The results are on my laptop, with gmpy (I'll try with Sage later):&lt;br /&gt;&lt;br /&gt;Firstly, results for &lt;tt&gt;gamma(z)&lt;/tt&gt;:&lt;br /&gt;&lt;br /&gt;&lt;table border="1" style="margin-left:1em;border-collapse:collapse"&gt;&lt;tr&gt;&lt;td&gt;&lt;i&gt;z&lt;/i&gt;&lt;/td&gt;&lt;td&gt;dps=15&lt;/td&gt;&lt;td&gt;dps=50&lt;/td&gt;&lt;td&gt;dps=250&lt;/td&gt;&lt;td&gt;dps=1000&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;3.0&lt;/td&gt;&lt;td&gt;0.0086&lt;br/&gt;0.0077&lt;br/&gt;(&lt;b&gt;1.13x&lt;/b&gt;)&lt;/td&gt;&lt;td&gt;0.0089&lt;br/&gt;0.0076&lt;br/&gt;(&lt;b&gt;1.16x&lt;/b&gt;)&lt;/td&gt;&lt;td&gt;0.0087&lt;br/&gt;0.0075&lt;br/&gt;(&lt;b&gt;1.16x&lt;/b&gt;)&lt;/td&gt;&lt;td&gt;0.0085&lt;br/&gt;0.0077&lt;br/&gt;(&lt;b&gt;1.12x&lt;/b&gt;)&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;5.27&lt;/td&gt;&lt;td&gt;0.1200&lt;br/&gt;0.0342&lt;br/&gt;(&lt;b&gt;3.51x&lt;/b&gt;)&lt;/td&gt;&lt;td&gt;0.2513&lt;br/&gt;0.0545&lt;br/&gt;(&lt;b&gt;4.61x&lt;/b&gt;)&lt;/td&gt;&lt;td&gt;2.5964&lt;br/&gt;0.4220&lt;br/&gt;(&lt;b&gt;6.15x&lt;/b&gt;)&lt;/td&gt;&lt;td&gt;80.7089&lt;br/&gt;11.3011&lt;br/&gt;(&lt;b&gt;7.14x&lt;/b&gt;)&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;2.5&lt;/td&gt;&lt;td&gt;0.1255&lt;br/&gt;0.0158&lt;br/&gt;(&lt;b&gt;7.94x&lt;/b&gt;)&lt;/td&gt;&lt;td&gt;0.2398&lt;br/&gt;0.0138&lt;br/&gt;(&lt;b&gt;17.41x&lt;/b&gt;)&lt;/td&gt;&lt;td&gt;2.5519&lt;br/&gt;0.0144&lt;br/&gt;(&lt;b&gt;177.72x&lt;/b&gt;)&lt;/td&gt;&lt;td&gt;79.0651&lt;br/&gt;0.0149&lt;br/&gt;(&lt;b&gt;5319.87x&lt;/b&gt;)&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;-3.7&lt;/td&gt;&lt;td&gt;0.2518&lt;br/&gt;0.0457&lt;br/&gt;(&lt;b&gt;5.51x&lt;/b&gt;)&lt;/td&gt;&lt;td&gt;0.4006&lt;br/&gt;0.0615&lt;br/&gt;(&lt;b&gt;6.52x&lt;/b&gt;)&lt;/td&gt;&lt;td&gt;3.1073&lt;br/&gt;0.4283&lt;br/&gt;(&lt;b&gt;7.26x&lt;/b&gt;)&lt;/td&gt;&lt;td&gt;87.4200&lt;br/&gt;11.3981&lt;br/&gt;(&lt;b&gt;7.67x&lt;/b&gt;)&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;(-3.0+4.0j)&lt;/td&gt;&lt;td&gt;0.6205&lt;br/&gt;0.3942&lt;br/&gt;(&lt;b&gt;1.57x&lt;/b&gt;)&lt;/td&gt;&lt;td&gt;1.0095&lt;br/&gt;0.6093&lt;br/&gt;(&lt;b&gt;1.66x&lt;/b&gt;)&lt;/td&gt;&lt;td&gt;9.0896&lt;br/&gt;5.9099&lt;br/&gt;(&lt;b&gt;1.54x&lt;/b&gt;)&lt;/td&gt;&lt;td&gt;253.9219&lt;br/&gt;191.3358&lt;br/&gt;(&lt;b&gt;1.33x&lt;/b&gt;)&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;(3.25-4.125j)&lt;/td&gt;&lt;td&gt;0.3548&lt;br/&gt;0.2864&lt;br/&gt;(&lt;b&gt;1.24x&lt;/b&gt;)&lt;/td&gt;&lt;td&gt;0.7456&lt;br/&gt;0.4791&lt;br/&gt;(&lt;b&gt;1.56x&lt;/b&gt;)&lt;/td&gt;&lt;td&gt;8.6651&lt;br/&gt;5.6367&lt;br/&gt;(&lt;b&gt;1.54x&lt;/b&gt;)&lt;/td&gt;&lt;td&gt;252.7598&lt;br/&gt;189.2187&lt;br/&gt;(&lt;b&gt;1.34x&lt;/b&gt;)&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;(15.0+20.0j)&lt;/td&gt;&lt;td&gt;0.3980&lt;br/&gt;0.1973&lt;br/&gt;(&lt;b&gt;2.02x&lt;/b&gt;)&lt;/td&gt;&lt;td&gt;0.7526&lt;br/&gt;0.4496&lt;br/&gt;(&lt;b&gt;1.67x&lt;/b&gt;)&lt;/td&gt;&lt;td&gt;8.6583&lt;br/&gt;5.4604&lt;br/&gt;(&lt;b&gt;1.59x&lt;/b&gt;)&lt;/td&gt;&lt;td&gt;250.8646&lt;br/&gt;188.5849&lt;br/&gt;(&lt;b&gt;1.33x&lt;/b&gt;)&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;52.1&lt;/td&gt;&lt;td&gt;0.1661&lt;br/&gt;0.0586&lt;br/&gt;(&lt;b&gt;2.83x&lt;/b&gt;)&lt;/td&gt;&lt;td&gt;0.3050&lt;br/&gt;0.0894&lt;br/&gt;(&lt;b&gt;3.41x&lt;/b&gt;)&lt;/td&gt;&lt;td&gt;2.9862&lt;br/&gt;0.5685&lt;br/&gt;(&lt;b&gt;5.25x&lt;/b&gt;)&lt;/td&gt;&lt;td&gt;84.8334&lt;br/&gt;12.4169&lt;br/&gt;(&lt;b&gt;6.83x&lt;/b&gt;)&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;123.25&lt;/td&gt;&lt;td&gt;0.1643&lt;br/&gt;0.0638&lt;br/&gt;(&lt;b&gt;2.57x&lt;/b&gt;)&lt;/td&gt;&lt;td&gt;0.3071&lt;br/&gt;0.0963&lt;br/&gt;(&lt;b&gt;3.19x&lt;/b&gt;)&lt;/td&gt;&lt;td&gt;2.8908&lt;br/&gt;0.9194&lt;br/&gt;(&lt;b&gt;3.14x&lt;/b&gt;)&lt;/td&gt;&lt;td&gt;83.8168&lt;br/&gt;14.6588&lt;br/&gt;(&lt;b&gt;5.72x&lt;/b&gt;)&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;-200.7&lt;/td&gt;&lt;td&gt;0.3011&lt;br/&gt;0.1485&lt;br/&gt;(&lt;b&gt;2.03x&lt;/b&gt;)&lt;/td&gt;&lt;td&gt;0.4897&lt;br/&gt;0.1914&lt;br/&gt;(&lt;b&gt;2.56x&lt;/b&gt;)&lt;/td&gt;&lt;td&gt;3.5145&lt;br/&gt;1.1359&lt;br/&gt;(&lt;b&gt;3.09x&lt;/b&gt;)&lt;/td&gt;&lt;td&gt;89.8617&lt;br/&gt;17.5500&lt;br/&gt;(&lt;b&gt;5.12x&lt;/b&gt;)&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;(100.25+100.0j)&lt;/td&gt;&lt;td&gt;0.4238&lt;br/&gt;0.1857&lt;br/&gt;(&lt;b&gt;2.28x&lt;/b&gt;)&lt;/td&gt;&lt;td&gt;0.7641&lt;br/&gt;0.2757&lt;br/&gt;(&lt;b&gt;2.77x&lt;/b&gt;)&lt;/td&gt;&lt;td&gt;8.6807&lt;br/&gt;3.1403&lt;br/&gt;(&lt;b&gt;2.76x&lt;/b&gt;)&lt;/td&gt;&lt;td&gt;249.6731&lt;br/&gt;171.4369&lt;br/&gt;(&lt;b&gt;1.46x&lt;/b&gt;)&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;12345678.9&lt;/td&gt;&lt;td&gt;0.3032&lt;br/&gt;0.0701&lt;br/&gt;(&lt;b&gt;4.32x&lt;/b&gt;)&lt;/td&gt;&lt;td&gt;0.4382&lt;br/&gt;0.0857&lt;br/&gt;(&lt;b&gt;5.11x&lt;/b&gt;)&lt;/td&gt;&lt;td&gt;3.4078&lt;br/&gt;0.3257&lt;br/&gt;(&lt;b&gt;10.46x&lt;/b&gt;)&lt;/td&gt;&lt;td&gt;88.4082&lt;br/&gt;5.2828&lt;br/&gt;(&lt;b&gt;16.74x&lt;/b&gt;)&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;(0.0+12345678.9j)&lt;/td&gt;&lt;td&gt;0.7814&lt;br/&gt;0.1409&lt;br/&gt;(&lt;b&gt;5.54x&lt;/b&gt;)&lt;/td&gt;&lt;td&gt;1.1671&lt;br/&gt;0.1802&lt;br/&gt;(&lt;b&gt;6.48x&lt;/b&gt;)&lt;/td&gt;&lt;td&gt;10.3985&lt;br/&gt;0.6169&lt;br/&gt;(&lt;b&gt;16.86x&lt;/b&gt;)&lt;/td&gt;&lt;td&gt;265.6192&lt;br/&gt;8.3569&lt;br/&gt;(&lt;b&gt;31.78x&lt;/b&gt;)&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;1.0e+20&lt;/td&gt;&lt;td&gt;0.8833&lt;br/&gt;0.0798&lt;br/&gt;(&lt;b&gt;11.06x&lt;/b&gt;)&lt;/td&gt;&lt;td&gt;1.3019&lt;br/&gt;0.0899&lt;br/&gt;(&lt;b&gt;14.48x&lt;/b&gt;)&lt;/td&gt;&lt;td&gt;6.4010&lt;br/&gt;0.2939&lt;br/&gt;(&lt;b&gt;21.78x&lt;/b&gt;)&lt;/td&gt;&lt;td&gt;107.0924&lt;br/&gt;3.5141&lt;br/&gt;(&lt;b&gt;30.47x&lt;/b&gt;)&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;(0.0+1.0e+20j)&lt;/td&gt;&lt;td&gt;3.5025&lt;br/&gt;0.1515&lt;br/&gt;(&lt;b&gt;23.11x&lt;/b&gt;)&lt;/td&gt;&lt;td&gt;3.6163&lt;br/&gt;0.1834&lt;br/&gt;(&lt;b&gt;19.72x&lt;/b&gt;)&lt;/td&gt;&lt;td&gt;19.5676&lt;br/&gt;0.5308&lt;br/&gt;(&lt;b&gt;36.86x&lt;/b&gt;)&lt;/td&gt;&lt;td&gt;322.9033&lt;br/&gt;6.2148&lt;br/&gt;(&lt;b&gt;51.96x&lt;/b&gt;)&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;br /&gt;&lt;br /&gt;Secondly, results for &lt;tt&gt;loggamma(z)&lt;/tt&gt;:&lt;br /&gt;&lt;br /&gt;&lt;table border="1" style="margin-left:1em;border-collapse:collapse"&gt;&lt;tr&gt;&lt;td&gt;&lt;i&gt;z&lt;/i&gt;&lt;/td&gt;&lt;td&gt;dps=15&lt;/td&gt;&lt;td&gt;dps=50&lt;/td&gt;&lt;td&gt;dps=250&lt;/td&gt;&lt;td&gt;dps=1000&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;3.0&lt;/td&gt;&lt;td&gt;0.0700&lt;br/&gt;0.0115&lt;br/&gt;(&lt;b&gt;6.07x&lt;/b&gt;)&lt;/td&gt;&lt;td&gt;0.0677&lt;br/&gt;0.0116&lt;br/&gt;(&lt;b&gt;5.84x&lt;/b&gt;)&lt;/td&gt;&lt;td&gt;0.0683&lt;br/&gt;0.0119&lt;br/&gt;(&lt;b&gt;5.73x&lt;/b&gt;)&lt;/td&gt;&lt;td&gt;0.0683&lt;br/&gt;0.0121&lt;br/&gt;(&lt;b&gt;5.65x&lt;/b&gt;)&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;5.27&lt;/td&gt;&lt;td&gt;0.2207&lt;br/&gt;0.0564&lt;br/&gt;(&lt;b&gt;3.91x&lt;/b&gt;)&lt;/td&gt;&lt;td&gt;0.3548&lt;br/&gt;0.0796&lt;br/&gt;(&lt;b&gt;4.45x&lt;/b&gt;)&lt;/td&gt;&lt;td&gt;2.8925&lt;br/&gt;0.5221&lt;br/&gt;(&lt;b&gt;5.54x&lt;/b&gt;)&lt;/td&gt;&lt;td&gt;88.3556&lt;br/&gt;12.6452&lt;br/&gt;(&lt;b&gt;6.99x&lt;/b&gt;)&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;2.5&lt;/td&gt;&lt;td&gt;0.2210&lt;br/&gt;0.0319&lt;br/&gt;(&lt;b&gt;6.93x&lt;/b&gt;)&lt;/td&gt;&lt;td&gt;0.3565&lt;br/&gt;0.0374&lt;br/&gt;(&lt;b&gt;9.53x&lt;/b&gt;)&lt;/td&gt;&lt;td&gt;2.8595&lt;br/&gt;0.1011&lt;br/&gt;(&lt;b&gt;28.28x&lt;/b&gt;)&lt;/td&gt;&lt;td&gt;81.8333&lt;br/&gt;1.3546&lt;br/&gt;(&lt;b&gt;60.41x&lt;/b&gt;)&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;-3.7&lt;/td&gt;&lt;td&gt;0.4392&lt;br/&gt;0.0782&lt;br/&gt;(&lt;b&gt;5.62x&lt;/b&gt;)&lt;/td&gt;&lt;td&gt;0.6418&lt;br/&gt;0.0987&lt;br/&gt;(&lt;b&gt;6.50x&lt;/b&gt;)&lt;/td&gt;&lt;td&gt;3.5767&lt;br/&gt;0.5429&lt;br/&gt;(&lt;b&gt;6.59x&lt;/b&gt;)&lt;/td&gt;&lt;td&gt;88.8484&lt;br/&gt;12.7363&lt;br/&gt;(&lt;b&gt;6.98x&lt;/b&gt;)&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;(-3.0+4.0j)&lt;/td&gt;&lt;td&gt;1.1235&lt;br/&gt;0.4942&lt;br/&gt;(&lt;b&gt;2.27x&lt;/b&gt;)&lt;/td&gt;&lt;td&gt;1.5685&lt;br/&gt;0.7224&lt;br/&gt;(&lt;b&gt;2.17x&lt;/b&gt;)&lt;/td&gt;&lt;td&gt;10.0879&lt;br/&gt;6.0459&lt;br/&gt;(&lt;b&gt;1.67x&lt;/b&gt;)&lt;/td&gt;&lt;td&gt;267.2893&lt;br/&gt;198.7613&lt;br/&gt;(&lt;b&gt;1.34x&lt;/b&gt;)&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;(3.25-4.125j)&lt;/td&gt;&lt;td&gt;0.8789&lt;br/&gt;0.2704&lt;br/&gt;(&lt;b&gt;3.25x&lt;/b&gt;)&lt;/td&gt;&lt;td&gt;1.2326&lt;br/&gt;0.4752&lt;br/&gt;(&lt;b&gt;2.59x&lt;/b&gt;)&lt;/td&gt;&lt;td&gt;9.7501&lt;br/&gt;5.4572&lt;br/&gt;(&lt;b&gt;1.79x&lt;/b&gt;)&lt;/td&gt;&lt;td&gt;264.7649&lt;br/&gt;190.4007&lt;br/&gt;(&lt;b&gt;1.39x&lt;/b&gt;)&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;(15.0+20.0j)&lt;/td&gt;&lt;td&gt;0.8983&lt;br/&gt;0.1248&lt;br/&gt;(&lt;b&gt;7.20x&lt;/b&gt;)&lt;/td&gt;&lt;td&gt;1.2923&lt;br/&gt;0.4221&lt;br/&gt;(&lt;b&gt;3.06x&lt;/b&gt;)&lt;/td&gt;&lt;td&gt;9.7917&lt;br/&gt;5.2766&lt;br/&gt;(&lt;b&gt;1.86x&lt;/b&gt;)&lt;/td&gt;&lt;td&gt;265.2711&lt;br/&gt;187.9775&lt;br/&gt;(&lt;b&gt;1.41x&lt;/b&gt;)&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;52.1&lt;/td&gt;&lt;td&gt;0.2604&lt;br/&gt;0.0812&lt;br/&gt;(&lt;b&gt;3.21x&lt;/b&gt;)&lt;/td&gt;&lt;td&gt;0.4186&lt;br/&gt;0.1140&lt;br/&gt;(&lt;b&gt;3.67x&lt;/b&gt;)&lt;/td&gt;&lt;td&gt;3.2493&lt;br/&gt;0.6890&lt;br/&gt;(&lt;b&gt;4.72x&lt;/b&gt;)&lt;/td&gt;&lt;td&gt;87.0111&lt;br/&gt;13.9280&lt;br/&gt;(&lt;b&gt;6.25x&lt;/b&gt;)&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;123.25&lt;/td&gt;&lt;td&gt;0.2629&lt;br/&gt;0.0478&lt;br/&gt;(&lt;b&gt;5.50x&lt;/b&gt;)&lt;/td&gt;&lt;td&gt;0.4227&lt;br/&gt;0.0692&lt;br/&gt;(&lt;b&gt;6.11x&lt;/b&gt;)&lt;/td&gt;&lt;td&gt;3.1110&lt;br/&gt;1.0216&lt;br/&gt;(&lt;b&gt;3.05x&lt;/b&gt;)&lt;/td&gt;&lt;td&gt;85.9028&lt;br/&gt;15.9917&lt;br/&gt;(&lt;b&gt;5.37x&lt;/b&gt;)&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;-200.7&lt;/td&gt;&lt;td&gt;0.5439&lt;br/&gt;0.1631&lt;br/&gt;(&lt;b&gt;3.34x&lt;/b&gt;)&lt;/td&gt;&lt;td&gt;0.7465&lt;br/&gt;0.2221&lt;br/&gt;(&lt;b&gt;3.36x&lt;/b&gt;)&lt;/td&gt;&lt;td&gt;3.9305&lt;br/&gt;1.2537&lt;br/&gt;(&lt;b&gt;3.14x&lt;/b&gt;)&lt;/td&gt;&lt;td&gt;92.5380&lt;br/&gt;18.8309&lt;br/&gt;(&lt;b&gt;4.91x&lt;/b&gt;)&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;(100.25+100.0j)&lt;/td&gt;&lt;td&gt;0.9103&lt;br/&gt;0.1179&lt;br/&gt;(&lt;b&gt;7.72x&lt;/b&gt;)&lt;/td&gt;&lt;td&gt;1.3332&lt;br/&gt;0.1849&lt;br/&gt;(&lt;b&gt;7.21x&lt;/b&gt;)&lt;/td&gt;&lt;td&gt;10.1655&lt;br/&gt;3.1083&lt;br/&gt;(&lt;b&gt;3.27x&lt;/b&gt;)&lt;/td&gt;&lt;td&gt;266.3565&lt;br/&gt;170.3099&lt;br/&gt;(&lt;b&gt;1.56x&lt;/b&gt;)&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;12345678.9&lt;/td&gt;&lt;td&gt;0.4026&lt;br/&gt;0.0491&lt;br/&gt;(&lt;b&gt;8.21x&lt;/b&gt;)&lt;/td&gt;&lt;td&gt;0.5439&lt;br/&gt;0.0525&lt;br/&gt;(&lt;b&gt;10.36x&lt;/b&gt;)&lt;/td&gt;&lt;td&gt;3.7183&lt;br/&gt;0.2051&lt;br/&gt;(&lt;b&gt;18.13x&lt;/b&gt;)&lt;/td&gt;&lt;td&gt;89.9402&lt;br/&gt;4.0044&lt;br/&gt;(&lt;b&gt;22.46x&lt;/b&gt;)&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;(0.0+12345678.9j)&lt;/td&gt;&lt;td&gt;1.2628&lt;br/&gt;0.0765&lt;br/&gt;(&lt;b&gt;16.52x&lt;/b&gt;)&lt;/td&gt;&lt;td&gt;1.7085&lt;br/&gt;0.0858&lt;br/&gt;(&lt;b&gt;19.91x&lt;/b&gt;)&lt;/td&gt;&lt;td&gt;11.5023&lt;br/&gt;0.2749&lt;br/&gt;(&lt;b&gt;41.84x&lt;/b&gt;)&lt;/td&gt;&lt;td&gt;275.6174&lt;br/&gt;4.4548&lt;br/&gt;(&lt;b&gt;61.87x&lt;/b&gt;)&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;1.0e+20&lt;/td&gt;&lt;td&gt;0.9820&lt;br/&gt;0.0495&lt;br/&gt;(&lt;b&gt;19.84x&lt;/b&gt;)&lt;/td&gt;&lt;td&gt;1.3882&lt;br/&gt;0.0540&lt;br/&gt;(&lt;b&gt;25.72x&lt;/b&gt;)&lt;/td&gt;&lt;td&gt;6.6983&lt;br/&gt;0.1481&lt;br/&gt;(&lt;b&gt;45.21x&lt;/b&gt;)&lt;/td&gt;&lt;td&gt;110.1757&lt;br/&gt;2.1954&lt;br/&gt;(&lt;b&gt;50.19x&lt;/b&gt;)&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;(0.0+1.0e+20j)&lt;/td&gt;&lt;td&gt;4.0385&lt;br/&gt;0.0788&lt;br/&gt;(&lt;b&gt;51.24x&lt;/b&gt;)&lt;/td&gt;&lt;td&gt;4.3758&lt;br/&gt;0.0833&lt;br/&gt;(&lt;b&gt;52.54x&lt;/b&gt;)&lt;/td&gt;&lt;td&gt;20.9478&lt;br/&gt;0.1936&lt;br/&gt;(&lt;b&gt;108.20x&lt;/b&gt;)&lt;/td&gt;&lt;td&gt;335.9407&lt;br/&gt;2.3911&lt;br/&gt;(&lt;b&gt;140.50x&lt;/b&gt;)&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;br /&gt;&lt;br /&gt;All times are from a warm cache. The new implementation has slightly longer precomputation time (for the Taylor series coefficients) at high precision, and the Taylor series therefore isn't used above a certain precision (around 1000 digits) despite being much faster on subsequent calls. I will probably add some user-visible function for controlling this.&lt;br /&gt;&lt;br /&gt;Finally, several optimizations are possible still. The most important would be to do the implementation in Cython. (Improving the underlying elementary functions will also speed up the gamma function.) Potential algorithmic improvements include faster handling of near-integers (using Taylor series with a reduced number of terms), avoiding the reflection formula for complex arguments in the left half-plane but not too close to the negative half-axis, and performing the argument transformations with a reduced number of multiplications. But I doubt I will have time (or interest -- the present code was demanding enough to finish) to work of any of these things, at least any time soon.&lt;br /&gt;&lt;br /&gt;The speedups naturally also affect performance of many other functions, e.g. hypergeometric functions; I might post some benchmarks of that later.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4754734402679928849-1865918379051735323?l=fredrik-j.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://fredrik-j.blogspot.com/feeds/1865918379051735323/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=4754734402679928849&amp;postID=1865918379051735323' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4754734402679928849/posts/default/1865918379051735323'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4754734402679928849/posts/default/1865918379051735323'/><link rel='alternate' type='text/html' href='http://fredrik-j.blogspot.com/2010/02/new-gamma-function-implementation.html' title='A new gamma function implementation'/><author><name>Fredrik Johansson</name><uri>http://www.blogger.com/profile/01465400860530971858</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4754734402679928849.post-6238455705356678132</id><published>2010-02-05T18:08:00.013+01:00</published><updated>2010-02-05T21:16:32.243+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='sage'/><category scheme='http://www.blogger.com/atom/ns#' term='sympy'/><category scheme='http://www.blogger.com/atom/ns#' term='mpmath'/><title type='text'>Mpmath 0.14 released</title><content type='html'>I've just released &lt;a href="http://code.google.com/p/mpmath/"&gt;version 0.14 of mpmath&lt;/a&gt;! Some highlights in this release have been covered in previous development updates on this blog:&lt;br /&gt;&lt;ul&gt;&lt;br /&gt;&lt;li&gt;&lt;a href="http://fredrik-j.blogspot.com/2010/02/mpmath-in-sage-to-become-3x-faster.html"&gt;mpmath in Sage to become 3x faster&lt;/a&gt; -- a Cython extension module will be added to Sage that greatly speeds up mpmath (the Sage patch is currently being reviewed; if all goes to plan, the patch will be accepted soon and the mpmath version in Sage will be updated to 0.14 at the same time).&lt;/li&gt;&lt;br /&gt;&lt;li&gt;&lt;a href="http://fredrik-j.blogspot.com/2010/01/zeta-evaluation-with-riemann-siegel.html"&gt;Zeta evaluation with the Riemann-Siegel expansion&lt;/a&gt; -- Juan Arias de Reyna contributed code for very efficient and robust evaluation of the Riemann zeta function for arguments with large imaginary part.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Various features discussed in &lt;a href="http://fredrik-j.blogspot.com/2010/01/yamdu-yet-another-mpmath-development.html"&gt;this update&lt;/a&gt;: 3D surface plotting (wrapping &lt;a href="http://matplotlib.sourceforge.net/"&gt;matplotlib&lt;/a&gt;, matrix calculus (transcendental functions of a matrix argument), Borel summation of divergent hypergeometric series, and options for whether to use Mathematica's conventions for hypergeometric functions have been added.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;&lt;a href="http://fredrik-j.blogspot.com/2010/01/accurate-hypergeometric-functions-for.html"&gt;Improved accuracy&lt;/a&gt; for hypergeometric functions with large parameters, and &lt;a href="http://fredrik-j.blogspot.com/2009/12/analytic-continuation-of-3f2-4f3-and.html"&gt;analytic continuation&lt;/a&gt; implemented for 3F2, 4F3, and higher hypergeometric functions.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Support for &lt;a href="http://fredrik-j.blogspot.com/2009/09/python-floats-and-other-unusual-things.html"&gt;using Python floats/complexes&lt;/a&gt; for faster low-precision math. This is very handy for plotting, multidimensional integration, and other things requiring many function evaluations.&lt;/li&gt;&lt;br /&gt;&lt;/ul&gt;There are many other changes as well. See the &lt;a href="http://mpmath.googlecode.com/svn/trunk/CHANGES"&gt;CHANGES&lt;/a&gt; file and the &lt;a href="http://code.google.com/p/mpmath/source/list"&gt;list of commits&lt;/a&gt; for more details. Thanks to Juan Arias de Reyna, Vinzent Steinberg, Jorn Baayen and Chris Smith who contributed patches, and various people who tested and submitted bug reports (thanks also to anyone else I forgot to mention).&lt;br /&gt;&lt;br /&gt;I'm sorry that it took several months to finish this release! This was partly due to large internal reorganizations done in order to support floats and the Cython backend in Sage. I've also had to take some time off to focus on my studies and other things.&lt;br /&gt;&lt;br /&gt;&lt;h3&gt;An example: spherical harmonics&lt;/h3&gt;&lt;br /&gt;Here I will demonstrate three new features with a single example: 3D plotting, one of the newly added special functions (spherical harmonics), and low-precision evaluation with the &lt;tt&gt;fp&lt;/tt&gt; context. Of course, everything here can be done in arbitrary precision with &lt;tt&gt;mp&lt;/tt&gt; as well; &lt;tt&gt;fp&lt;/tt&gt; is just faster for plotting.&lt;br /&gt;&lt;br /&gt;The following code visualizes spherical harmonics using a 3D polar plot. I've chosen to visualize the real part; the code is easily edited to show the absolute value (for example) instead.&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;from mpmath import fp&lt;br /&gt;&lt;br /&gt;def Y(m,n):&lt;br /&gt;    def g(theta,phi):&lt;br /&gt;        R = fp.re(fp.spherharm(m,n,theta,phi))**2&lt;br /&gt;        x = R*fp.cos(phi)*fp.sin(theta)&lt;br /&gt;        y = R*fp.sin(phi)*fp.sin(theta)&lt;br /&gt;        z = R*fp.cos(theta)&lt;br /&gt;        return [x,y,z]&lt;br /&gt;    return g&lt;br /&gt;&lt;br /&gt;fp.splot(Y(0,0), [0,fp.pi], [0,2*fp.pi])&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;The first few spherical harmonics are thus:&lt;br /&gt;&lt;table&gt;&lt;tr&gt;&lt;td&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://1.bp.blogspot.com/_rh0QblLk0C0/S2xZ-tQIiZI/AAAAAAAAAM8/BlgFTnnghMQ/s1600-h/00.png"&gt;&lt;img style="cursor:pointer; cursor:hand;width: 200px; height: 150px;" src="http://1.bp.blogspot.com/_rh0QblLk0C0/S2xZ-tQIiZI/AAAAAAAAAM8/BlgFTnnghMQ/s200/00.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5434817784163371410" /&gt;&lt;/a&gt;&lt;center&gt;Y(0,0)&lt;/center&gt;&lt;/td&gt;&lt;td&gt;&lt;/td&gt;&lt;td&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_rh0QblLk0C0/S2xaHqc7V7I/AAAAAAAAANE/kUyu3RIrCdo/s1600-h/10.png"&gt;&lt;img style="cursor:pointer; cursor:hand;width: 200px; height: 150px;" src="http://4.bp.blogspot.com/_rh0QblLk0C0/S2xaHqc7V7I/AAAAAAAAANE/kUyu3RIrCdo/s200/10.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5434817938030548914" /&gt;&lt;/a&gt;&lt;center&gt;Y(1,0)&lt;/center&gt;&lt;/td&gt;&lt;td&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://1.bp.blogspot.com/_rh0QblLk0C0/S2xaH41_o2I/AAAAAAAAANM/w2_vYmRrnQY/s1600-h/11.png"&gt;&lt;img style="cursor:pointer; cursor:hand;width: 200px; height: 150px;" src="http://1.bp.blogspot.com/_rh0QblLk0C0/S2xaH41_o2I/AAAAAAAAANM/w2_vYmRrnQY/s200/11.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5434817941893784418" /&gt;&lt;/a&gt;&lt;center&gt;Y(1,1)&lt;/center&gt;&lt;/td&gt;&lt;td&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://1.bp.blogspot.com/_rh0QblLk0C0/S2xaIEf6noI/AAAAAAAAANU/Fpt-tJ2ybZ0/s1600-h/20.png"&gt;&lt;img style="cursor:pointer; cursor:hand;width: 200px; height: 150px;" src="http://1.bp.blogspot.com/_rh0QblLk0C0/S2xaIEf6noI/AAAAAAAAANU/Fpt-tJ2ybZ0/s200/20.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5434817945022406274" /&gt;&lt;/a&gt;&lt;center&gt;Y(2,0)&lt;/center&gt;&lt;/td&gt;&lt;td&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_rh0QblLk0C0/S2xaIa4rSXI/AAAAAAAAANc/30GBPtb-ShE/s1600-h/21.png"&gt;&lt;img style="cursor:pointer; cursor:hand;width: 200px; height: 150px;" src="http://3.bp.blogspot.com/_rh0QblLk0C0/S2xaIa4rSXI/AAAAAAAAANc/30GBPtb-ShE/s200/21.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5434817951031839090" /&gt;&lt;/a&gt;&lt;center&gt;Y(2,1)&lt;/center&gt;&lt;/td&gt;&lt;td&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_rh0QblLk0C0/S2xaIdubpXI/AAAAAAAAANk/p13EdTaUB8g/s1600-h/22.png"&gt;&lt;img style="cursor:pointer; cursor:hand;width: 200px; height: 150px;" src="http://3.bp.blogspot.com/_rh0QblLk0C0/S2xaIdubpXI/AAAAAAAAANk/p13EdTaUB8g/s200/22.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5434817951794177394" /&gt;&lt;/a&gt;&lt;center&gt;Y(2,2)&lt;/center&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_rh0QblLk0C0/S2xaQs0vodI/AAAAAAAAANs/j3uuhJ-Fg50/s1600-h/30.png"&gt;&lt;img style="cursor:pointer; cursor:hand;width: 200px; height: 150px;" src="http://4.bp.blogspot.com/_rh0QblLk0C0/S2xaQs0vodI/AAAAAAAAANs/j3uuhJ-Fg50/s200/30.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5434818093286138322" /&gt;&lt;/a&gt;&lt;center&gt;Y(3,0)&lt;/center&gt;&lt;/td&gt;&lt;td&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_rh0QblLk0C0/S2xaQ_GJFuI/AAAAAAAAAN0/9hta30bucdg/s1600-h/31.png"&gt;&lt;img style="cursor:pointer; cursor:hand;width: 200px; height: 150px;" src="http://3.bp.blogspot.com/_rh0QblLk0C0/S2xaQ_GJFuI/AAAAAAAAAN0/9hta30bucdg/s200/31.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5434818098190948066" /&gt;&lt;/a&gt;&lt;center&gt;Y(3,1)&lt;/center&gt;&lt;/td&gt;&lt;td&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://2.bp.blogspot.com/_rh0QblLk0C0/S2xaRH8370I/AAAAAAAAAN8/COl6H95WCEg/s1600-h/32.png"&gt;&lt;img style="cursor:pointer; cursor:hand;width: 200px; height: 150px;" src="http://2.bp.blogspot.com/_rh0QblLk0C0/S2xaRH8370I/AAAAAAAAAN8/COl6H95WCEg/s200/32.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5434818100567994178" /&gt;&lt;/a&gt;&lt;center&gt;Y(3,2)&lt;/center&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;/td&gt;&lt;td&gt;&lt;/td&gt;&lt;td&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://2.bp.blogspot.com/_rh0QblLk0C0/S2xaRdbiK4I/AAAAAAAAAOE/cKpuaoj2q88/s1600-h/33.png"&gt;&lt;img style="cursor:pointer; cursor:hand;width: 200px; height: 150px;" src="http://2.bp.blogspot.com/_rh0QblLk0C0/S2xaRdbiK4I/AAAAAAAAAOE/cKpuaoj2q88/s200/33.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5434818106333735810" /&gt;&lt;/a&gt;&lt;center&gt;Y(3,3)&lt;/center&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;br /&gt;&lt;br /&gt;&lt;h3&gt;Some numerical examples&lt;/h3&gt;&lt;br /&gt;&lt;br /&gt;A selection of evaluations that were not implemented, failed, gave inaccurate results or were extremely slow in previous versions of mpmath:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;&gt;&gt;&gt; from mpmath import *&lt;br /&gt;&gt;&gt;&gt; mp.dps = 25; mp.pretty = True&lt;br /&gt;&lt;br /&gt;# reflection formula for Barnes G-function&lt;br /&gt;&gt;&gt;&gt; barnesg(-100000+10000j)&lt;br /&gt;(-7.332534002219787256775675e+22857579769 +&lt;br /&gt;  1.304469872717249495403882e+22857579770j)&lt;br /&gt;&lt;br /&gt;# Riemann zeta function, large height&lt;br /&gt;&gt;&gt;&gt; zeta(0.5+100000000j)&lt;br /&gt;(-3.362839487530727943146807 + 1.407234559646447885979583j)&lt;br /&gt;&lt;br /&gt;# Accurate evaluation of large-degree Bernoulli polynomial&lt;br /&gt;&gt;&gt;&gt; bernpoly(1000,100)&lt;br /&gt;4.360903799140670486890619e+1996&lt;br /&gt;&lt;br /&gt;# Computation of Euler numbers&lt;br /&gt;&gt;&gt;&gt; eulernum(20)&lt;br /&gt;370371188237525.0&lt;br /&gt;&gt;&gt;&gt; eulernum(50, exact=True)&lt;br /&gt;-6053285248188621896314383785111649088103498225146815121L&lt;br /&gt;&gt;&gt;&gt; eulernum(2000000000000000000)&lt;br /&gt;3.19651108713502662532039e+35341231273461153426&lt;br /&gt;&lt;br /&gt;# Accurate evaluation of hypergeometric functions with large parameters&lt;br /&gt;&gt;&gt;&gt; hyp2f1(1000,50,25,-1)&lt;br /&gt;-2.886992344761576738138864e-274&lt;br /&gt;&gt;&gt;&gt; legenp(0.5, 300, 0.25)&lt;br /&gt;-1.717508549497252387888262e+578&lt;br /&gt;&gt;&gt;&gt; besseli(2, 10, derivative=100)&lt;br /&gt;821.2386210064833486609255&lt;br /&gt;&lt;br /&gt;# Evaluation of 4F3 and 4F2&lt;br /&gt;&gt;&gt;&gt; hyper([1,2.125,3.25,4.5], [5.25,6.5,7], 1+2j)&lt;br /&gt;(1.006737556189825231039221 + 0.3058770612264986390003873j)&lt;br /&gt;&gt;&gt;&gt; hyper([1,2.125,3.25,4.5], [5.25,6.5], 1+2j)&lt;br /&gt;(0.3220085070641791195103201 + 0.5251231752161637627314328j)&lt;br /&gt;&lt;br /&gt;# Matrix functions&lt;br /&gt;&gt;&gt;&gt; A = matrix([[2,3,1+j],[1,0,-1],[2,1,5]])&lt;br /&gt;&gt;&gt;&gt; mnorm(A - logm(expm(A)))&lt;br /&gt;9.540212722670391941827867e-25&lt;br /&gt;&gt;&gt;&gt; mnorm(A - expm(logm(A)))&lt;br /&gt;4.375286550134565812513542e-26&lt;br /&gt;&gt;&gt;&gt; nprint(expm(2*A))&lt;br /&gt;[ (-2855.4 + 10733.0j)   (-1871.95 + 8597.9j)  (-7721.42 + 12906.7j)]&lt;br /&gt;[(-3910.68 - 1749.82j)  (-3152.69 - 1272.11j)  (-4460.18 - 3558.54j)]&lt;br /&gt;[ (17039.0 + 22621.8j)   (14320.3 + 17405.9j)   (13587.7 + 35087.8j)]&lt;br /&gt;&gt;&gt;&gt; nprint(expm(A)**2)&lt;br /&gt;[ (-2855.4 + 10733.0j)   (-1871.95 + 8597.9j)  (-7721.42 + 12906.7j)]&lt;br /&gt;[(-3910.68 - 1749.82j)  (-3152.69 - 1272.11j)  (-4460.18 - 3558.54j)]&lt;br /&gt;[ (17039.0 + 22621.8j)   (14320.3 + 17405.9j)   (13587.7 + 35087.8j)]&lt;br /&gt;&gt;&gt;&gt; nprint(chop(sqrtm(A)*sqrtm(A)))&lt;br /&gt;[2.0  3.0  (1.0 + 1.0j)]&lt;br /&gt;[1.0  0.0          -1.0]&lt;br /&gt;[2.0  1.0           5.0]&lt;br /&gt;&lt;br /&gt;# A convenience function for precise evaluation of exp(j*pi*x)&lt;br /&gt;&gt;&gt;&gt; expjpi(36); exp(j*pi*36)&lt;br /&gt;(1.0 + 0.0j)&lt;br /&gt;(1.0 + 4.702307256907087875509661e-25j)&lt;br /&gt;&lt;br /&gt;# Some fp functions have specialized implementations&lt;br /&gt;# for improved speed and accuracy&lt;br /&gt;&gt;&gt;&gt; fp.erfc(5); float(mp.erfc(5))&lt;br /&gt;1.5374597944280347e-12&lt;br /&gt;1.5374597944280349e-12&lt;br /&gt;&gt;&gt;&gt; fp.erf(5); float(mp.erf(5))&lt;br /&gt;0.99999999999846256&lt;br /&gt;0.99999999999846256&lt;br /&gt;&gt;&gt;&gt; fp.ei(-12-3j); complex(mp.ei(-12-3j))&lt;br /&gt;(4.6085637890261843e-07-3.141592693919709j)&lt;br /&gt;(4.6085637890261901e-07-3.141592693919709j)&lt;br /&gt;&gt;&gt;&gt; fp.zeta(-0.5); float(fp.zeta(-0.5))  # real-argument fp.zeta&lt;br /&gt;-0.20788622497735468&lt;br /&gt;-0.20788622497735468&lt;br /&gt;&gt;&gt;&gt; fp.ci(40); float(mp.ci(40))&lt;br /&gt;0.019020007896208769&lt;br /&gt;0.019020007896208765&lt;br /&gt;&gt;&gt;&gt; fp.gamma(4+5j); complex(mp.gamma(4+5j))&lt;br /&gt;(0.14965532796078551+0.31460331072967695j)&lt;br /&gt;(0.14965532796078521+0.31460331072967596j)&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;For more examples and images, see the posts linked at the top of this post!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4754734402679928849-6238455705356678132?l=fredrik-j.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://fredrik-j.blogspot.com/feeds/6238455705356678132/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=4754734402679928849&amp;postID=6238455705356678132' title='4 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4754734402679928849/posts/default/6238455705356678132'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4754734402679928849/posts/default/6238455705356678132'/><link rel='alternate' type='text/html' href='http://fredrik-j.blogspot.com/2010/02/mpmath-014-released.html' title='Mpmath 0.14 released'/><author><name>Fredrik Johansson</name><uri>http://www.blogger.com/profile/01465400860530971858</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://1.bp.blogspot.com/_rh0QblLk0C0/S2xZ-tQIiZI/AAAAAAAAAM8/BlgFTnnghMQ/s72-c/00.png' height='72' width='72'/><thr:total>4</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4754734402679928849.post-4288714427975980764</id><published>2010-02-01T01:00:00.013+01:00</published><updated>2010-02-03T03:04:15.578+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='sage'/><category scheme='http://www.blogger.com/atom/ns#' term='sympy'/><category scheme='http://www.blogger.com/atom/ns#' term='mpmath'/><title type='text'>mpmath in Sage to become 3x faster</title><content type='html'>I've blogged previously about the potential speedups attainable by rewriting core parts of mpmath in Cython (&lt;a href="http://fredrik-j.blogspot.com/2009/05/cython-mpmath-performance.html"&gt;here&lt;/a&gt; and &lt;a href="http://fredrik-j.blogspot.com/2009/06/cython-mpmath-performance-part-2.html"&gt;here&lt;/a&gt;), but all I had back then was some standalone classes and functions that couldn't easily be integrated. Well, I now finally have a fully working, fully integrated Cython implementation of the &lt;tt&gt;mpf&lt;/tt&gt; and &lt;tt&gt;mpc&lt;/tt&gt; types (plus related utility functions) -- and it successfully runs the entire mpmath test suite!&lt;br /&gt;&lt;br /&gt;On my computer (Ubuntu 64 bit, AMD Athlon 64 3700+), running &lt;tt&gt;import mpmath; mpmath.runtests()&lt;/tt&gt; in Sage takes this much time:&lt;br /&gt;&lt;br /&gt;Before [mpmath (svn trunk) + Sage 4.3.1]: &lt;b&gt;62.75 seconds&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;After [mpmath (svn trunk with modifications) + Cython mpmath types + Sage 4.3.1 + a &lt;a href="http://trac.sagemath.org/sage_trac/ticket/6199"&gt;Sage performance patch&lt;/a&gt; by Robert Bradshaw]: &lt;b&gt;19.87 seconds&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;I have essentially only implemented arithmetic operations and conversions, so core transcendental functions (as well as square roots and powers) still fall back to the Python code, and they account for the majority of the running time. According to cProfile, about 20% of the total time is spent in &lt;tt&gt;exp()&lt;/tt&gt; and &lt;tt&gt;gamma()&lt;/tt&gt; alone. Hypergeometric series evaluation is also still done in Python. Replacing these functions with Cython versions should give a significant boost, and will be very easy to do in the future (in terms of code infrastructure). So realistically, the running time for the unit tests can be cut much further, probably in half.&lt;br /&gt;&lt;br /&gt;The code isn't public yet. I will soon commit the changes to the mpmath svn repository and create a patch for Sage with the Cython extensions. The code just needs a little more cleanup, and there are no doubt a couple of subtle issues (such as corner-case memory leaks) that need fixing... but essentially, it's working, and the tests pass, so I expect it to be releasable quite soon.&lt;br /&gt;&lt;br /&gt;&lt;i&gt;Update: after adding just a little more Cythonized wrapper code, the time is now down to 17.64 seconds. I have still not touched exp, log, cos, sin, gamma, or any other core transcendental functions. Expect further improvements.&lt;/i&gt;&lt;br /&gt;&lt;br /&gt;&lt;i&gt;Update 2 (2010-02-03): preliminary version of the Cython code &lt;a href="http://trac.sagemath.org/sage_trac/ticket/8159"&gt;here&lt;/a&gt;&lt;/i&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4754734402679928849-4288714427975980764?l=fredrik-j.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://fredrik-j.blogspot.com/feeds/4288714427975980764/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=4754734402679928849&amp;postID=4288714427975980764' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4754734402679928849/posts/default/4288714427975980764'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4754734402679928849/posts/default/4288714427975980764'/><link rel='alternate' type='text/html' href='http://fredrik-j.blogspot.com/2010/02/mpmath-in-sage-to-become-3x-faster.html' title='mpmath in Sage to become 3x faster'/><author><name>Fredrik Johansson</name><uri>http://www.blogger.com/profile/01465400860530971858</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4754734402679928849.post-7542417770939437388</id><published>2010-01-27T19:14:00.005+01:00</published><updated>2010-01-27T20:27:17.577+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='sage'/><category scheme='http://www.blogger.com/atom/ns#' term='sympy'/><category scheme='http://www.blogger.com/atom/ns#' term='mpmath'/><title type='text'>Using Sage numbers in mpmath</title><content type='html'>I've written a very basic mpmath context for computing "natively" with Sage's real and complex numbers. It can be tried out by upgrading mpmath in Sage to the svn trunk, applying &lt;a href="http://boxen.math.washington.edu/home/fredrik/mpsage/fixes.diff"&gt;this patch&lt;/a&gt; this patch to fix the helper code in Sage, and then importing &lt;a href="http://boxen.math.washington.edu/home/fredrik/mpsage/mpsage.py"&gt;this file&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;Some examples:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;----------------------------------------------------------------------&lt;br /&gt;| Sage Version 4.3.1, Release Date: 2010-01-20                       |&lt;br /&gt;| Type notebook() for the GUI, and license() for information.        |&lt;br /&gt;----------------------------------------------------------------------&lt;br /&gt;sage: from mpsage import SageContext&lt;br /&gt;sage: from mpmath import mp, timing&lt;br /&gt;sage: mp.prec = 150&lt;br /&gt;sage: sc = SageContext(150)&lt;br /&gt;sage: &lt;br /&gt;sage: print sc.quad(lambda t: sc.exp(-t**2), [0,sc.inf])&lt;br /&gt;0.88622692545275801364908374167057259139877471&lt;br /&gt;sage: print mp.quad(lambda t: mp.exp(-t**2), [0,mp.inf])&lt;br /&gt;0.88622692545275801364908374167057259139877473&lt;br /&gt;sage: timing(sc.quad, lambda t: sc.exp(-t**2), [0,sc.inf])&lt;br /&gt;0.40903496742248535&lt;br /&gt;sage: timing(mp.quad, lambda t: mp.exp(-t**2), [0,mp.inf])&lt;br /&gt;0.43610286712646484&lt;br /&gt;sage: &lt;br /&gt;sage: print sc.zeta(4+5*I)&lt;br /&gt;0.95121830700949569861514024576822624312072806 + 0.024932824507357722259855155244740571939365897*I&lt;br /&gt;sage: print mp.zeta(4+5*I, method='euler-maclaurin')&lt;br /&gt;(0.95121830700949569861514024576822624312072806 + 0.024932824507357722259855155244740571939365897j)&lt;br /&gt;sage: timing(sc.zeta, 4+5*I)&lt;br /&gt;0.015111517906188966&lt;br /&gt;sage: timing(mp.zeta, 4+5*I, method='euler-maclaurin')&lt;br /&gt;0.028736209869384764&lt;br /&gt;sage: &lt;br /&gt;sage: print sc.zeta(1+100000000*I)&lt;br /&gt;1.8349308953278466065175598876062914152382527 + 1.0691847904236128969174476736978194200591565*I&lt;br /&gt;sage: print mp.zeta(1+100000000*I)&lt;br /&gt;(1.8349308953278466065175598876062914152563156 + 1.069184790423612896917447673697819420203942j)&lt;br /&gt;sage: timing(sc.zeta, 1+100000000*I)&lt;br /&gt;1.0952680110931396&lt;br /&gt;sage: timing(mp.zeta, 1+100000000*I)&lt;br /&gt;2.0537140369415283&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Unfortunately, there are some issues (besides the fact that some methods are missing, so not everything works yet).&lt;br /&gt;&lt;br /&gt;This context doesn't provide variable precision, so the user has to manually allocate sufficient extra precision to compensate for rounding errors.&lt;br /&gt;&lt;br /&gt;I first tried to do it, but variable precision is very inconvenient to implement using Sage's way of managing precision. There is no direct way to perform operations with a given target precision (independent of the inputs), and the second best option (which is to perform coercions to the target precision everywhere) is very slow, besides losing accuracy. The only way to implement variable precision in a reasonable way is to bypass Sage's &lt;tt&gt;RealNumber&lt;/tt&gt; and &lt;tt&gt;ComplexNumber&lt;/tt&gt; (at least their public interfaces) and wrap MPFR directly using a precision model similar to what MPFR and mpmath uses, where the precision of the result is always specified and independent of the inputs.&lt;br /&gt;&lt;br /&gt;Secondly, there is not that much of a speedup (in the examples above, the speedup is at most about 2x). This is mainly due to the fact that the context uses wrapper classes for &lt;tt&gt;RealNumber&lt;/tt&gt; and &lt;tt&gt;ComplexNumber&lt;/tt&gt;, with all interface and conversion code written in Python. So the overhead is about the same as for the corresponding code in vanilla mpmath (where it accounts for about 50% of the total overhead). The reason &lt;tt&gt;RealNumber&lt;/tt&gt; and &lt;tt&gt;ComplexNumber&lt;/tt&gt; can't be used directly, even in a fixed-precision setting, is that mpmath in many places multiplies numbers by floats (usually exact float literals like 0.5), and Sage always coerces to the number with &lt;i&gt;less&lt;/i&gt; precision. This could presumably be fixed by replacing all &lt;tt&gt;float&lt;/tt&gt; and &lt;tt&gt;complex&lt;/tt&gt; constants in mpmath, but I'm not in the mood to do that right now.&lt;br /&gt;&lt;br /&gt;There should be a significant performance benefit if direct-from-Cython classes and conversion methods were used. It's also very important to optimize certain core functions; for example, quadrature would benefit greatly from a fast &lt;tt&gt;fdot&lt;/tt&gt; implementation.&lt;br /&gt;&lt;br /&gt;All things considered, I'm probably not going to continue much more down this particular path. It's better to write a fully compatible Cython context with classes designed directly for mpmath. Trying to wrap Sage's numbers did however help identify a few problems with the existing interfaces, so I might extend the work on this context a little more just to find more such issues.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4754734402679928849-7542417770939437388?l=fredrik-j.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://fredrik-j.blogspot.com/feeds/7542417770939437388/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=4754734402679928849&amp;postID=7542417770939437388' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4754734402679928849/posts/default/7542417770939437388'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4754734402679928849/posts/default/7542417770939437388'/><link rel='alternate' type='text/html' href='http://fredrik-j.blogspot.com/2010/01/using-sage-numbers-in-mpmath.html' title='Using Sage numbers in mpmath'/><author><name>Fredrik Johansson</name><uri>http://www.blogger.com/profile/01465400860530971858</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4754734402679928849.post-5107160762395221376</id><published>2010-01-19T14:35:00.007+01:00</published><updated>2010-01-19T22:38:21.009+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='sage'/><category scheme='http://www.blogger.com/atom/ns#' term='sympy'/><category scheme='http://www.blogger.com/atom/ns#' term='mpmath'/><title type='text'>Zeta evaluation with the Riemann-Siegel expansion</title><content type='html'>I'm very grateful to &lt;a href="http://personal.us.es/arias/"&gt;Juan Arias de Reyna&lt;/a&gt; who has contributed &lt;a href="http://code.google.com/p/mpmath/source/detail?r=1033"&gt;a module&lt;/a&gt; implementing zeta function evaluation using the &lt;a href="http://en.wikipedia.org/wiki/Riemann%E2%80%93Siegel_formula"&gt;Riemann-Siegel formula&lt;/a&gt; in mpmath. This finally permits rapid evaluation of the Riemann zeta function for arguments with large imaginary part.&lt;br /&gt;&lt;br /&gt;To follow tradition on this blog, pictorial proof shall be given. Here is a plot of a segment of the critical line, &amp;zeta;(1/2+&lt;i&gt;t&lt;/i&gt;i) with &lt;i&gt;t&lt;/i&gt; between 10&lt;sup&gt;10&lt;/sup&gt;+50 and 10&lt;sup&gt;10&lt;/sup&gt;+55:&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://2.bp.blogspot.com/_rh0QblLk0C0/S1XBljEsmXI/AAAAAAAAAMk/-lAbE7oolIU/s1600-h/zetaline.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 400px; height: 176px;" src="http://2.bp.blogspot.com/_rh0QblLk0C0/S1XBljEsmXI/AAAAAAAAAMk/-lAbE7oolIU/s400/zetaline.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5428457776679262578" /&gt;&lt;/a&gt;&lt;br /&gt;A complex plot of showing the critical strip, &lt;i&gt;t&lt;/i&gt; ranging between 10&lt;sup&gt;8&lt;/sup&gt;+40 and 10&lt;sup&gt;8&lt;/sup&gt;+45 (note: the &lt;i&gt;y&lt;/i&gt; scale is reversed):&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://1.bp.blogspot.com/_rh0QblLk0C0/S1XVpCU9vOI/AAAAAAAAAM0/LPTLQkplWTw/s1600-h/strip.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 400px; height: 111px;" src="http://1.bp.blogspot.com/_rh0QblLk0C0/S1XVpCU9vOI/AAAAAAAAAM0/LPTLQkplWTw/s400/strip.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5428479826841156834" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Juan Arias de Reyna, who is a professor of mathematics at the University of Seville, has done a thorough job with this code. He has even proved rigorous error bounds for his algorithm (subject to assumptions that the underlying functions in mpmath being well-implemented). The news is that the bounds -- documented in an as-yet unpublished paper -- are valid off the critical line. The code also computes derivatives (up to 4th derivatives), although not as rigorously but still very robustly.&lt;br /&gt;&lt;br /&gt;I integrated the code (and added a few optimizations) during the last few days. The zeta function in mpmath now automatically switches between Borwein's algorithm (close to the real line), Euler-Maclaurin summation, and the Riemann-Siegel expansion.&lt;br /&gt;&lt;br /&gt;Some example values and timings on my laptop, computing &amp;zeta;(1/2+10&lt;sup&gt;&lt;i&gt;n&lt;/i&gt;&lt;/sup&gt;i) for &lt;i&gt;n&lt;/i&gt; up to 12:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;&gt;&gt;&gt; from timeit import default_timer as clock&lt;br /&gt;&gt;&gt;&gt; mp.dps = 25&lt;br /&gt;&gt;&gt;&gt;&lt;br /&gt;&gt;&gt;&gt; for n in range(13):&lt;br /&gt;...     t1 = clock(); y = zeta(0.5 + 10**n*j); t2 = clock()&lt;br /&gt;...     print n, y, round(t2-t1,3)&lt;br /&gt;...&lt;br /&gt;0 (0.1439364270771890603243897 - 0.7220997435316730891261751j) 0.02&lt;br /&gt;1 (1.544895220296752766921496 - 0.1153364652712733754365914j) 0.003&lt;br /&gt;2 (2.692619885681324090476096 - 0.02038602960259816177072685j) 0.042&lt;br /&gt;3 (0.3563343671943960550744025 + 0.9319978312329936651150604j) 0.073&lt;br /&gt;4 (-0.3393738026388344575674711 - 0.03709150597320603147434421j) 0.434&lt;br /&gt;5 (1.073032014857753132114076 + 5.780848544363503984261041j) 0.167&lt;br /&gt;6 (0.07608906973822710000556456 + 2.805102101019298955393837j) 0.146&lt;br /&gt;7 (11.45804061057709254500227 - 8.643437226836021723818215j) 0.181&lt;br /&gt;8 (-3.362839487530727943146807 + 1.407234559646447885979583j) 0.336&lt;br /&gt;9 (-2.761748029838060942376813 - 1.677512240989459839213205j) 0.877&lt;br /&gt;10 (0.3568002308560733825395879 + 0.286505849095836103292093j) 2.695&lt;br /&gt;11 (0.6436639255801185727194357 + 0.1168615914616853418448829j) 8.583&lt;br /&gt;12 (2.877961809278403355251079 - 3.206771071318398923493412j) 26.934&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Similar results off the critical line:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;&gt;&gt;&gt; for n in range(13):&lt;br /&gt;...     t1 = clock(); y = zeta(1.0 + 10**n*j); t2 = clock()&lt;br /&gt;...     print n, y, round(t2-t1,3)&lt;br /&gt;...&lt;br /&gt;0 (0.5821580597520036481994632 - 0.9268485643308070765364243j) 0.021&lt;br /&gt;1 (1.390287313237401426796005 - 0.109785153066302056909746j) 0.004&lt;br /&gt;2 (1.632833506686711866610705 - 0.06813120384181249010120548j) 0.043&lt;br /&gt;3 (0.9409368682927533108010138 + 0.04522665207209509908865644j) 0.083&lt;br /&gt;4 (0.4973279229716308441790286 - 0.5878238243194009766923214j) 0.598&lt;br /&gt;5 (1.618122122846936796567759 + 1.070441041470623686626035j) 0.233&lt;br /&gt;6 (0.9473872625104789104802422 + 0.5942199931209183283333071j) 0.195&lt;br /&gt;7 (2.859846483332530337008882 + 0.491808047480981808903986j) 0.409&lt;br /&gt;8 (1.83493089532784660651756 + 1.069184790423612896917448j) 0.455&lt;br /&gt;9 (0.9038018561650776977609945 - 1.189857828822373901473908j) 1.393&lt;br /&gt;10 (0.5418173564211820524034624 + 0.635303581895880322679247j) 3.824&lt;br /&gt;11 (0.5365466615361310937110304 - 0.1234443975100346650640542j) 12.031&lt;br /&gt;12 (0.8225630719679733497367277 - 0.4484762282040223492401122j) 38.061&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;The implementation also supports use of the &lt;tt&gt;fp&lt;/tt&gt; context. Double precision unavoidably becomes insufficient as the imaginary part approaches 10&lt;sup&gt;15&lt;/sup&gt;, but it has the advantage of speed in the range where it works:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;&gt;&gt;&gt; for n in range(13):&lt;br /&gt;...     t1 = clock(); y = fp.zeta(0.5 + 10**n*1j); t2 = clock()&lt;br /&gt;...     print n, y, round(t2-t1,3)&lt;br /&gt;...&lt;br /&gt;0 (0.143936427077-0.722099743532j) 0.007&lt;br /&gt;1 (1.5448952203-0.115336465271j) 0.001&lt;br /&gt;2 (2.69261988568-0.0203860296026j) 0.003&lt;br /&gt;3 (0.356334367195+0.931997831233j) 0.004&lt;br /&gt;4 (-0.339373802616-0.0370915059691j) 0.123&lt;br /&gt;5 (1.07303201485+5.78084854433j) 0.005&lt;br /&gt;6 (0.076089072636+2.80510210471j) 0.006&lt;br /&gt;7 (11.4580404601-8.64343725721j) 0.006&lt;br /&gt;8 (-3.36283920435+1.40723433071j) 0.011&lt;br /&gt;9 (-2.76174796643-1.67750591108j) 0.028&lt;br /&gt;10 (0.356829034142+0.286525774475j) 0.083&lt;br /&gt;11 (0.64314751322+0.116713738713j) 0.256&lt;br /&gt;12 (2.8689206645-3.21135962379j) 0.808&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;We have done some comparison with Mathematica, and the mpmath version appears to be about as fast (a bit faster or a bit slower, sometimes substantially faster, depending on circumstance). The most expensive part of the computation occurs in a simple internal function that adds lots of &lt;i&gt;n&lt;/i&gt;&lt;sup&gt;&amp;minus;&lt;i&gt;s&lt;/i&gt;&lt;/sup&gt; terms. I think for Sage, it will be very easy to switch to a Cython version of this function which should improve speed by a large factor.&lt;br /&gt;&lt;br /&gt;But most importantly, Mathematica's &lt;tt&gt;Zeta[]&lt;/tt&gt; is notoriously buggy for large imaginary arguments. As a first example, here Mathematica 7.0 computes two entirely different values at different precisions:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;In[3]:= N[Zeta[1/4+10^12 I], 15]&lt;br /&gt;Out[3]= -0.0125397 + 0.0139723 I&lt;br /&gt;In[4]:= N[Zeta[1/4+10^12 I], 30]&lt;br /&gt;Out[4]= 358.066828240154490750947835567 - 580.623633400912069466146291259 I&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;With mpmath:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;&gt;&gt;&gt; mp.dps = 15; zeta(0.25+1e12j)&lt;br /&gt;(358.066828240154 - 580.623633400912j)&lt;br /&gt;&gt;&gt;&gt; mp.dps = 30; zeta(0.25+1e12j)&lt;br /&gt;(358.066828240154490750947835567 - 580.623633400912069466146291259j)&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;As a second example, if Mathematica is asked for derivatives, it's more likely than not to return complete nonsense, and even increasing the precision doesn't help:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;In[2]:= N[Zeta'[1/2+10^6 I], 15]&lt;br /&gt;&lt;br /&gt;                           883                      883&lt;br /&gt;Out[2]= 2.48728166483172 10    - 7.66644043045624 10    I&lt;br /&gt;&lt;br /&gt;In[3]:= N[Zeta'[1/2+10^6 I], 25]&lt;br /&gt;&lt;br /&gt;                                     940                                940&lt;br /&gt;Out[3]= 1.586685034587255948191759 10    + 2.158475809806136995106119 10    I&lt;br /&gt;&lt;br /&gt;In[4]:= N[Zeta'[1/2+10^6 I], 30]&lt;br /&gt;&lt;br /&gt;                                            1022&lt;br /&gt;Out[4]= -1.071044014417407205715473623855 10     -&lt;br /&gt;&lt;br /&gt;                                       1021&lt;br /&gt;&gt;    2.73478073192015960107298455871 10     I&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;For contrast, mpmath computes derivatives perfectly:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;&gt;&gt;&gt; mp.dps = 15; zeta(0.5+1e6j, derivative=1)&lt;br /&gt;(11.6368040660025 - 17.127254072213j)&lt;br /&gt;&gt;&gt;&gt; mp.dps = 25; zeta(0.5+1e6j, derivative=1)&lt;br /&gt;(11.63680406600252145919591 - 17.12725407221299600357895j)&lt;br /&gt;&gt;&gt;&gt; mp.dps = 30; zeta(0.5+1e6j, derivative=1)&lt;br /&gt;(11.6368040660025214591959071246 - 17.1272540722129960035789468265j)&lt;br /&gt;&gt;&gt;&gt; diff(zeta, 0.5+1e6j)  # Numerical verification&lt;br /&gt;(11.6368040660025214591959071246 - 17.1272540722129960035789468265j)&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;That's all for now. I'm back in school again, so maybe I won't have as much time for programming in the near future. But my schedule is flexible, so we'll see. A new release of mpmath shouldn't be far away.&lt;br /&gt;&lt;br /&gt;&lt;i&gt;Update: I should point out that the bugs in Mathematica's Zeta[] only seem to occur in recent versions. Mathematica 5 and older versions do not seem to have these problems.&lt;/i&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4754734402679928849-5107160762395221376?l=fredrik-j.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://fredrik-j.blogspot.com/feeds/5107160762395221376/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=4754734402679928849&amp;postID=5107160762395221376' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4754734402679928849/posts/default/5107160762395221376'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4754734402679928849/posts/default/5107160762395221376'/><link rel='alternate' type='text/html' href='http://fredrik-j.blogspot.com/2010/01/zeta-evaluation-with-riemann-siegel.html' title='Zeta evaluation with the Riemann-Siegel expansion'/><author><name>Fredrik Johansson</name><uri>http://www.blogger.com/profile/01465400860530971858</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://2.bp.blogspot.com/_rh0QblLk0C0/S1XBljEsmXI/AAAAAAAAAMk/-lAbE7oolIU/s72-c/zetaline.png' height='72' width='72'/><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4754734402679928849.post-5446147940821430798</id><published>2010-01-13T11:32:00.009+01:00</published><updated>2010-01-13T13:54:50.232+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='sage'/><category scheme='http://www.blogger.com/atom/ns#' term='sympy'/><category scheme='http://www.blogger.com/atom/ns#' term='mpmath'/><title type='text'>YAMDU (yet another mpmath development update)</title><content type='html'>At the moment, I'm able to get quite a bit of work done on mpmath. This includes general code cleanup, gardening the issue tracker, and finishing features that were works-in-progress for a long while.&lt;br /&gt;&lt;br /&gt;Today's topics are:&lt;br /&gt;&lt;br /&gt;3D plotting&lt;br /&gt;Matrix calculus&lt;br /&gt;Borel summation of divergent hypergeometric series&lt;br /&gt;Optional consistency with Mathematica&lt;br /&gt;Internal reorganization&lt;br /&gt;&lt;br /&gt;&lt;h4&gt;3D plotting&lt;/h4&gt;&lt;br /&gt;Jorn Baayen submitted a patch that adds 3D surface plotting (along with some other minor changes to the visualization module). Thanks a lot! I &lt;a href="http://fredrik-j.blogspot.com/2009/08/3d-visualization-of-complex-functions.html"&gt;previously blogged&lt;/a&gt; about 3D plotting using matplotlib.&lt;br /&gt;&lt;br /&gt;You can now easily produce plots of functions of the form &lt;i&gt;z&lt;/i&gt; = &lt;i&gt;f&lt;/i&gt;(&lt;i&gt;x&lt;/i&gt;,&lt;i&gt;y&lt;/i&gt;):&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;splot(lambda x, y: 10*sinc(hypot(x,y)), [-10,10], [-10,10])&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_rh0QblLk0C0/S02jKNwAS6I/AAAAAAAAAMM/Kk_Ao6c9t0k/s1600-h/sinc.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 400px; height: 302px;" src="http://4.bp.blogspot.com/_rh0QblLk0C0/S02jKNwAS6I/AAAAAAAAAMM/Kk_Ao6c9t0k/s400/sinc.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5426172521936341922" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;You can just as easily plot more general parametric functions &lt;i&gt;x&lt;/i&gt;,&lt;i&gt;y&lt;/i&gt;,&lt;i&gt;z&lt;/i&gt; = &lt;i&gt;f&lt;/i&gt;(&lt;i&gt;u&lt;/i&gt;,&lt;i&gt;v&lt;/i&gt;). The following plots a &lt;a href="http://en.wikipedia.org/wiki/M%C3%B6bius_strip"&gt;Möbius strip&lt;/a&gt;:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;def f(u,v):&lt;br /&gt;    d = (1+0.5*v*cos(0.5*u))&lt;br /&gt;    x = d*cos(u)&lt;br /&gt;    y = d*sin(u)&lt;br /&gt;    z = 0.5*v*sin(0.5*u)&lt;br /&gt;    return x,y,z&lt;br /&gt;&lt;br /&gt;splot(f, [0,2*pi], [-1,1])&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_rh0QblLk0C0/S02hRnaFbmI/AAAAAAAAAL8/n0PxpUebzSg/s1600-h/moebius.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 400px; height: 302px;" src="http://3.bp.blogspot.com/_rh0QblLk0C0/S02hRnaFbmI/AAAAAAAAAL8/n0PxpUebzSg/s400/moebius.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5426170450059554402" /&gt;&lt;/a&gt;&lt;br /&gt;(I'm not sure why there are black spots in the image. This seems to be a matplotlib bug.)&lt;br /&gt;&lt;br /&gt;&lt;h4&gt;Matrix calculus&lt;/h4&gt;&lt;br /&gt;It's now possible to compute exponentials, sines, cosines, square roots, logarithms, and complex powers (&lt;i&gt;A&lt;/i&gt;&lt;sup&gt;&lt;i&gt;z&lt;/i&gt;&lt;/sup&gt;) of square matrices:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;&gt;&gt;&gt; mp.dps = 25; mp.pretty = True&lt;br /&gt;&gt;&gt;&gt; A = matrix([[2,3,1+j],[1,0,-1],[2,1,5]])&lt;br /&gt;&gt;&gt;&gt; nprint(expm(A))&lt;br /&gt;[ (27.6034 + 41.2728j)   (24.9924 + 31.1189j)     (8.67256 + 68.271j)]&lt;br /&gt;[(-15.0962 + 1.51713j)  (-10.1713 + 1.30035j)  (-28.7003 - 0.401789j)]&lt;br /&gt;[ (125.368 + 37.7417j)   (98.9812 + 26.9693j)    (158.616 + 85.1593j)]&lt;br /&gt;&gt;&gt;&gt; nprint(logm(expm(A)))&lt;br /&gt;[(2.0 - 5.35398e-26j)           (3.0 + 6.9172e-26j)           (1.0 + 1.0j)]&lt;br /&gt;[(1.0 + 4.94422e-26j)  (4.42102e-25 - 1.14411e-25j)  (-1.0 - 1.95948e-27j)]&lt;br /&gt;[(2.0 + 1.58966e-26j)          (1.0 - 8.57028e-27j)   (5.0 + 3.23653e-27j)]&lt;br /&gt;&gt;&gt;&gt; nprint(sqrtm(A)**2)&lt;br /&gt;[(2.0 - 8.25839e-30j)          (3.0 - 8.40399e-30j)           (1.0 + 1.0j)]&lt;br /&gt;[(1.0 + 4.63764e-30j)  (4.84564e-30 - 3.27721e-31j)  (-1.0 + 7.47261e-31j)]&lt;br /&gt;[(2.0 - 4.31871e-30j)          (1.0 + 1.78726e-31j)   (5.0 + 2.54582e-29j)]&lt;br /&gt;&gt;&gt;&gt; nprint(powm(powm(A,1+j),1/(1+j)))&lt;br /&gt;[(2.0 - 1.12995e-26j)          (3.0 - 8.93158e-27j)           (1.0 + 1.0j)]&lt;br /&gt;[(1.0 - 1.54352e-26j)  (9.23906e-27 - 1.67262e-26j)  (-1.0 - 2.62243e-27j)]&lt;br /&gt;[(2.0 + 9.97431e-27j)          (1.0 + 1.56341e-26j)   (5.0 - 1.79194e-26j)]&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;The code also works with the double precision &lt;tt&gt;fp&lt;/tt&gt; context, which obviously is much faster for large matrices. When computing square roots and logarithms, most of the time is spent on matrix inversion, which can be accelerated by substituting in numpy:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;&gt;&gt;&gt; from mpmath import fp&lt;br /&gt;&gt;&gt;&gt; A = fp.randmatrix(20)&lt;br /&gt;&gt;&gt;&gt; B = fp.sqrtm(A)&lt;br /&gt;&gt;&gt;&gt; timing(fp.sqrtm, A)&lt;br /&gt;3.6470590535948006&lt;br /&gt;&gt;&gt;&gt;&lt;br /&gt;&gt;&gt;&gt; import numpy&lt;br /&gt;&gt;&gt;&gt; fp.inverse = lambda A: fp.matrix(numpy.linalg.inv(A.tolist()))&lt;br /&gt;&gt;&gt;&gt;&lt;br /&gt;&gt;&gt;&gt; timing(fp.sqrtm, A)&lt;br /&gt;1.0815329881311735&lt;br /&gt;&gt;&gt;&gt; C = fp.sqrtm(A)&lt;br /&gt;&gt;&gt;&gt;&lt;br /&gt;&gt;&gt;&gt; fp.mnorm(A-B**2)&lt;br /&gt;3.5001514923930131e-14&lt;br /&gt;&gt;&gt;&gt; fp.mnorm(A-C**2)&lt;br /&gt;2.6462503380426079e-14&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;It could be much faster still by doing everything in numpy. Probably in the future &lt;tt&gt;fp&lt;/tt&gt; should be improved to seamlessly use numpy internally for all available matrix operations. Patches are welcome!&lt;br /&gt;&lt;br /&gt;&lt;h4&gt;Borel summation of divergent hypergeometric series&lt;/h4&gt;&lt;br /&gt;The hypergeometric series of degree &lt;i&gt;p&lt;/i&gt; &amp;gt; &lt;i&gt;q&lt;/i&gt;+1 are divergent for |&lt;i&gt;z&lt;/i&gt;| &amp;gt; 0. Nevertheless, they define asymptotic expansions for analytic functions which exist in the sense of &lt;a href="http://en.wikipedia.org/wiki/Borel_summation"&gt;Borel summation&lt;/a&gt;. Previously, mpmath knew only how to evaluate &lt;sub&gt;2&lt;/sub&gt;F&lt;sub&gt;0&lt;/sub&gt; (whose Borel sum has a closed form), but now it can evaluate the functions of higher degree as well.&lt;br /&gt;&lt;br /&gt;To illustrate, the cosine integral Ci(&lt;i&gt;z&lt;/i&gt;) has an asymptotic series representation in terms of &lt;sub&gt;3&lt;/sub&gt;F&lt;sub&gt;0&lt;/sub&gt;:&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_rh0QblLk0C0/S02p_AsYJ8I/AAAAAAAAAMU/W87rjCSYbmQ/s1600-h/cosint.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 400px; height: 28px;" src="http://4.bp.blogspot.com/_rh0QblLk0C0/S02p_AsYJ8I/AAAAAAAAAMU/W87rjCSYbmQ/s400/cosint.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5426180026034300866" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;This representation is efficient for very large values of |&lt;i&gt;z&lt;/i&gt;|, where the argument of the hypergeometric series is small. But with &lt;i&gt;z&lt;/i&gt; = 0.5, say, the series does not yield a single digit. With a value of &lt;i&gt;z&lt;/i&gt; = 10 or so, the series only gives about 3 digits with an optimal truncation:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;&gt;&gt;&gt; z = 10.0&lt;br /&gt;&gt;&gt;&gt; for n in range(1,16):&lt;br /&gt;...     print sum(rf(0.5,k)*rf(1,k)*rf(1,k)*(-4/(z**2))**k/fac(k) for k in range(n))&lt;br /&gt;...&lt;br /&gt;1.0&lt;br /&gt;0.98&lt;br /&gt;0.9824&lt;br /&gt;0.98168&lt;br /&gt;0.9820832&lt;br /&gt;0.98172032&lt;br /&gt;0.9821993216&lt;br /&gt;0.981327538688&lt;br /&gt;0.9834198176768&lt;br /&gt;0.977017443971072&lt;br /&gt;1.00134646405284&lt;br /&gt;0.888946391275078&lt;br /&gt;1.50939479300832&lt;br /&gt;-2.52351981825774&lt;br /&gt;27.9653146429137&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Of course, there are ways to compute the cosine integral using convergent series. But if we pretend that we only know about the asymptotic series, then the Borel regularization means that we can evaluate the function to high precision even for small &lt;i&gt;z&lt;/i&gt;:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;&gt;&gt;&gt; mp.dps = 25; mp.pretty = True&lt;br /&gt;&gt;&gt;&gt; z = mpf(0.5)&lt;br /&gt;&gt;&gt;&gt; u = -4/z**2&lt;br /&gt;&gt;&gt;&gt; H1 = hyper([1,1,1.5],[],u)&lt;br /&gt;&gt;&gt;&gt; H2 = hyper([0.5,1,1],[],u)&lt;br /&gt;&gt;&gt;&gt;&lt;br /&gt;&gt;&gt;&gt; print log(z)-log(z**2)/2-cos(z)/z**2*H1+sin(z)/z*H2&lt;br /&gt;-0.1777840788066129013358103&lt;br /&gt;&gt;&gt;&gt; print ci(z)&lt;br /&gt;-0.1777840788066129013358103&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;The &lt;i&gt;z&lt;/i&gt; = 10 series above, to high precision:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;&gt;&gt;&gt; hyper([0.5,1,1],[],-4/(10.0**2))&lt;br /&gt;0.9819103501017016869905255&lt;br /&gt;&gt;&gt;&gt; mp.dps = 40&lt;br /&gt;&gt;&gt;&gt; hyper([0.5,1,1],[],-4/(10.0**2))&lt;br /&gt;0.9819103501017016869905255431829554224704&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;It's instructive to visualize the optimal truncations of an asymptotic series compared to the exact solution. Notice how, for an alternating series like this, the truncations alternate between over- and undershooting:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;def optimal_asymp(z):&lt;br /&gt;    u = -4/(z**2)&lt;br /&gt;    term_prev = inf&lt;br /&gt;    s = 0.0&lt;br /&gt;    for k in range(30):&lt;br /&gt;        term = rf(0.5,k)*rf(1,k)*rf(1,k)*u**k/fac(k)&lt;br /&gt;        if abs(term) &lt; abs(term_prev):&lt;br /&gt;            s += term&lt;br /&gt;            term_prev = term&lt;br /&gt;        else:&lt;br /&gt;            break&lt;br /&gt;    return s&lt;br /&gt;&lt;br /&gt;def exact(z):&lt;br /&gt;    u = -4/(z**2)&lt;br /&gt;    return hyper([0.5,1,1],[],u)&lt;br /&gt;&lt;br /&gt;mp.dps = 10&lt;br /&gt;plot([optimal_asymp, exact], [0,10])&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_rh0QblLk0C0/S029qzTFuEI/AAAAAAAAAMc/tBlHaFbcbw8/s1600-h/asymp.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 400px; height: 302px;" src="http://4.bp.blogspot.com/_rh0QblLk0C0/S029qzTFuEI/AAAAAAAAAMc/tBlHaFbcbw8/s400/asymp.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5426201669073745986" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;The catch with this feature? Computing the Borel regularization involves evaluating (nested) numerical integrals with a hypergeometric function in the integrand. Except for special parameters (where degree reductions happen internally -- the Ci series above happens to be such a case), it's not very fast.&lt;br /&gt;&lt;br /&gt;&lt;h4&gt;Optional consistency with Mathematica&lt;/h4&gt;&lt;br /&gt;I often try to follow Mathematica's conventions regarding limits, special values and branch cuts of special functions. This simplifies testing (I can directly compare values with Mathematica), and usually Mathematica's conventions seem well-reasoned.&lt;br /&gt;&lt;br /&gt;There are exceptions, however. One such exception concerns Mathematica's interpretation of &lt;sub&gt;2&lt;/sub&gt;F&lt;sub&gt;1&lt;/sub&gt;(&lt;i&gt;a&lt;/i&gt;,&lt;i&gt;b&lt;/i&gt;,&lt;i&gt;c&lt;/i&gt;,&lt;i&gt;z&lt;/i&gt;) for &lt;i&gt;b&lt;/i&gt; = &lt;i&gt;c&lt;/i&gt; a negative integer. Mathematica interprets this as a polynomial, which doesn't make much sense since the denominator of the zero term is zero. It's not even consistent with how Mathematica evaluates this function for a symbolic variable:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;In[3]:= Hypergeometric2F1[3,-2,-2,2]&lt;br /&gt;&lt;br /&gt;Out[3]= 31&lt;br /&gt;&lt;br /&gt;In[4]:= Hypergeometric2F1[3,b,b,2]&lt;br /&gt;&lt;br /&gt;Out[4]= -1&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Maybe there is a good reason for doing what Mathematica does, but it's at the very least not documented anywhere. I've now changed mpmath to instead interpret the two parameters as eliminating each other and giving a &lt;sub&gt;1&lt;/sub&gt;F&lt;sub&gt;0&lt;/sub&gt; function (which is what Mathematica does for a generic value). The Mathematica-compatible value can be recovered by explicitly disabling parameter elimination:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;&gt;&gt;&gt; mp.pretty = True&lt;br /&gt;&gt;&gt;&gt;&lt;br /&gt;&gt;&gt;&gt; hyp2f1(3,-2,-2,2)&lt;br /&gt;-1.0&lt;br /&gt;&gt;&gt;&gt; hyp2f1(3,-2,-2,2,eliminate=False)&lt;br /&gt;31.0&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;On a related note, I've fixed the Meijer G-function to switch between its &lt;i&gt;z&lt;/i&gt; and 1/&lt;i&gt;z&lt;/i&gt; forms automatically to follow Mathematica's definition of the function. This introduces discontinuities in the function for certain orders. The user can explicitly choose which form to use (so as to obtain a continuous function) with &lt;tt&gt;series=1&lt;/tt&gt; or &lt;tt&gt;series=2&lt;/tt&gt;.&lt;br /&gt;&lt;br /&gt;&lt;h4&gt;Internal reorganization&lt;/h4&gt;&lt;br /&gt;In a long overdue change, I've moved many of the modules in mpmath into subdirectories. The multiprecision library code is now located in mpmath/libmp; special functions are in mpmath/functions, calculus routines are in mpmath/calculus, and routines for matrices and linear algebra are in mpmath/matrices.&lt;br /&gt;&lt;br /&gt;This shouldn't affect any documented interfaces, but it will break external code that directly uses mpmath internal functions. The mpmath interfaces in SymPy and Sage will need some editing for the next version update. The breakage should be straightforward to fix (mostly just a matter of changing imports).&lt;br /&gt;&lt;br /&gt;Since the next version of mpmath is definitely going to break some code, I might use the opportunity to do some other cosmetic interface changes as well. Ideally, after the next version, the interface will be stable until and beyond mpmath 1.0 (whenever that happens). But the version number is still at 0.x for a reason -- no compatibility guarantees.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4754734402679928849-5446147940821430798?l=fredrik-j.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://fredrik-j.blogspot.com/feeds/5446147940821430798/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=4754734402679928849&amp;postID=5446147940821430798' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4754734402679928849/posts/default/5446147940821430798'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4754734402679928849/posts/default/5446147940821430798'/><link rel='alternate' type='text/html' href='http://fredrik-j.blogspot.com/2010/01/yamdu-yet-another-mpmath-development.html' title='YAMDU (yet another mpmath development update)'/><author><name>Fredrik Johansson</name><uri>http://www.blogger.com/profile/01465400860530971858</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://4.bp.blogspot.com/_rh0QblLk0C0/S02jKNwAS6I/AAAAAAAAAMM/Kk_Ao6c9t0k/s72-c/sinc.png' height='72' width='72'/><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4754734402679928849.post-5515174066457792351</id><published>2010-01-07T13:43:00.005+01:00</published><updated>2010-01-07T13:54:36.497+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='sage'/><category scheme='http://www.blogger.com/atom/ns#' term='sympy'/><category scheme='http://www.blogger.com/atom/ns#' term='mpmath'/><title type='text'>Accurate hypergeometric functions for large parameters</title><content type='html'>Holiday season means more free time for coding (yay!). In &lt;a href="http://code.google.com/p/mpmath/source/detail?r=1002"&gt;this commit&lt;/a&gt;, I've fixed the remaining major accuracy issues with hypergeometric functions in mpmath. The effect, generally speaking, is that mpmath will now deal much more robustly with large parameters of hypergeometric-type functions.&lt;br /&gt;&lt;br /&gt;Previously, you would obtain an accurate value with parameters of magnitude &amp;asymp; 10&lt;sup&gt;&amp;minus;1&lt;/sup&gt; - 10&lt;sup&gt;1&lt;/sup&gt; (say), and often for large parameters as well, but for some functions the accuracy would quickly deteriorate if parameters were increased (or moved closer to singularities). You could still obtain any accuracy by simply increasing the working precision, but you had to manually figure out the amount of extra precision required; the news is that mpmath now automatically gives a value with full accuracy even for large parameters (or screams out loud if it fails to compute an accurate value).&lt;br /&gt;&lt;br /&gt;This doesn't mean that you can't trick mpmath into computing a wrong value by choosing sufficiently evil parameters, but it's much harder now than simply plugging in large values. Abnormally large values will now mainly accomplish abnormal slowness (while mpmath implements asymptotic expansions with respect to the argument of hypergeometric functions, evaluation for asymptotically large parameters is a much harder problem as far as I'm aware -- so mpmath in effect accomplishes it by brute force.)&lt;br /&gt;&lt;br /&gt;The most trivial change was to change the series summation to aim for control of the relative error instead of the absolute error. This affects alternating series, where large parameters lead to very small function values. Previously, something like the following would happen:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;    &gt;&gt;&gt; mp.dps=5; hyp1f1(-5000, 1500, 100)&lt;br /&gt;    1.6353e-14&lt;br /&gt;    &gt;&gt;&gt; mp.dps=15; hyp1f1(-5000, 1500, 100)&lt;br /&gt;    8.09813050863682e-25&lt;br /&gt;    &gt;&gt;&gt; mp.dps=30; hyp1f1(-5000, 1500, 100)&lt;br /&gt;    -1.38318247777802583583082760724e-39&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;This isn't disastrously bad since the absolute error is controlled (summing this series naively in floating-point arithmetic might give something like 1e+234 due to catastrophic cancellation). But now, you get the relatively accurate answer right away, which is much nicer:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;    &gt;&gt;&gt; mp.dps = 5; hyp1f1(-5000, 1500, 100)&lt;br /&gt;    9.1501e-185&lt;br /&gt;    &gt;&gt;&gt; mp.dps = 15; hyp1f1(-5000, 1500, 100)&lt;br /&gt;    9.15012134245639e-185&lt;br /&gt;    &gt;&gt;&gt; mp.dps = 30; hyp1f1(-5000, 1500, 100)&lt;br /&gt;    9.15012134245639443114220499541e-185&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Of course, if the value is too small, the evaluation loop must abort eventually. The default behavior now is to raise an exception if relative accuracy cannot be obtained. The user can force a return value by either permitting a higher internal precision before aborting, or specifying a size 2&lt;sup&gt;&amp;minus;zeroprec&lt;/sup&gt; below which the value is small enough to be considered zero:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;    &gt;&gt;&gt; hyp1f1(-8000, 4000, 1500)&lt;br /&gt;    Traceback (most recent call last):&lt;br /&gt;      ...&lt;br /&gt;    ValueError: hypsum() failed to converge to the requested 53 bits of accuracy&lt;br /&gt;    using a working precision of 3568 bits. Try with a higher maxprec,&lt;br /&gt;    maxterms, or set zeroprec.&lt;br /&gt;    &gt;&gt;&gt;&lt;br /&gt;    &gt;&gt;&gt;&lt;br /&gt;    &gt;&gt;&gt; hyp1f1(-8000, 4000, 1500, maxprec=10000)&lt;br /&gt;    4.36754212717293e-1350&lt;br /&gt;    &gt;&gt;&gt; hyp1f1(-8000, 4000, 1500, accurate_small=False)&lt;br /&gt;    -1.99286380611911e-25&lt;br /&gt;    &gt;&gt;&gt; hyp1f1(-8000, 4000, 1500, zeroprec=1000)&lt;br /&gt;    0.0&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Since exceptions are inconvenient, it will be necessary to add some more symbolic tests for exact zeros for certain functions (particularly orthogonal polynomials) where exact zeros arise as important special cases.&lt;br /&gt;&lt;br /&gt;Also, cancellation detection in &lt;tt&gt;hypercomb&lt;/tt&gt; is now the default for all functions. This fixes, among other things, a bug in the Meijer G-function reported by a user via email. Before:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;    &gt;&gt;&gt; meijerg([[],[]],[[0],[]],0.1)&lt;br /&gt;    0.90483741803596&lt;br /&gt;    &gt;&gt;&gt; meijerg([[],[]],[[0,0],[]],0.1)&lt;br /&gt;    1.47347419562219&lt;br /&gt;    &gt;&gt;&gt; meijerg([[],[]],[[0,0,0],[]],0.1)&lt;br /&gt;    1.61746025085449&lt;br /&gt;    &gt;&gt;&gt; meijerg([[],[]],[[0,0,0,0],[]],0.1)   # suspicious&lt;br /&gt;    13194139533312.0&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;After:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;    &gt;&gt;&gt; meijerg([[],[]],[[0],[]],0.1)&lt;br /&gt;    0.90483741803596&lt;br /&gt;    &gt;&gt;&gt; meijerg([[],[]],[[0,0],[]],0.1)&lt;br /&gt;    1.47347419562219&lt;br /&gt;    &gt;&gt;&gt; meijerg([[],[]],[[0,0,0],[]],0.1)&lt;br /&gt;    1.61745972140487&lt;br /&gt;    &gt;&gt;&gt; meijerg([[],[]],[[0,0,0,0],[]],0.1)&lt;br /&gt;    1.56808223438324&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Another important change is more correct handling parameters very close to negative integers, particularly those appearing in denominators of series. Previously, unless the integer &lt;i&gt;n&lt;/i&gt; was small enough or the precision was set high enough, this situation would yield a bogus value (that of a prematurely truncated series):&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;    &gt;&gt;&gt; n = -20&lt;br /&gt;    &gt;&gt;&gt; nh = fadd(n, 1e-100, exact=True)&lt;br /&gt;    &gt;&gt;&gt; hyp0f1(n, 0.25)    # nonsense&lt;br /&gt;    0.987581857511351&lt;br /&gt;    &gt;&gt;&gt; hyp0f1(nh, 0.25)   # same nonsense&lt;br /&gt;    0.987581857511351&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Now, correctly:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;    &gt;&gt;&gt; n = -20&lt;br /&gt;    &gt;&gt;&gt; nh = fadd(n, 1e-100, exact=True)&lt;br /&gt;    &gt;&gt;&gt; hyp0f1(n, 0.25)&lt;br /&gt;    Traceback (most recent call last):&lt;br /&gt;      ...&lt;br /&gt;    ZeroDivisionError: pole in hypergeometric series&lt;br /&gt;    &gt;&gt;&gt; hyp0f1(nh, 0.25)&lt;br /&gt;    1.85014429040103e+49&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Finally, and probably most importantly, the limit evaluation code has been changed to adaptively decrease the size of perturbation until convergence. Under fairly general assumptions, the maximum accurate perturbation at precision &lt;i&gt;p&lt;/i&gt; can easily be shown to be 2&lt;sup&gt;&amp;minus;(&lt;i&gt;p&lt;/i&gt;+&lt;i&gt;C&lt;/i&gt;)&lt;/sup&gt;; the problem is that the parameter-dependent constant &lt;i&gt;C&lt;/i&gt; isn't generally known. Previously &lt;i&gt;C&lt;/i&gt; was just set to a small value (10), and naturally this would break down for some functions when parameters were increased beyond roughly that magnitude.&lt;br /&gt;&lt;br /&gt;I briefly considered the idea of estimating &lt;i&gt;C&lt;/i&gt; analytically in terms of the parameters, and while I think this can be done rigorously, it seems difficult -- especially to do it tightly (grossly overestimating &lt;i&gt;C&lt;/i&gt; would murder performance). The algorithm implemented now is quasi-rigorous, and although there is some slowdown (sometimes by a fair amount), the improved reliability is definitely worth it. A user can also manually supply a perturbation size of their own taste, thereby overriding adaptivity. If the user supplies an intelligent value, this gives both the speed of the old code and full rigor. Probably some intelligent choices for particular functions could be made automatically by mpmath too, to recover the old speed in common cases.&lt;br /&gt;&lt;br /&gt;An example of a situation where this becomes necessary is for Legendre functions with certain large parameter combinations. With the old code:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;    &gt;&gt;&gt; mp.dps = 15; legenp(0.5, 300, 0.25)   # bad&lt;br /&gt;    4.19317029788723e+626&lt;br /&gt;    &gt;&gt;&gt; mp.dps = 30; legenp(0.5, 300, 0.25)   # bad&lt;br /&gt;    3.72428336871098039162972441784e+611&lt;br /&gt;    &gt;&gt;&gt; mp.dps = 60; legenp(0.5, 300, 0.25)   # bad&lt;br /&gt;    2.93794154954090326636196697693611381787845107728298382876544e+581&lt;br /&gt;    &gt;&gt;&gt; mp.dps = 100; legenp(0.5, 300, 0.25)   # only accurate to a few digits&lt;br /&gt;    -1.71750854949725238788826203712778687036438365374945625996246145924802366061559&lt;br /&gt;    6579520831362887006032e+578&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;With the new code:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;    &gt;&gt;&gt; mp.dps = 15; legenp(0.5, 300, 0.25)&lt;br /&gt;    -1.71750854949725e+578&lt;br /&gt;    &gt;&gt;&gt; mp.dps = 30; legenp(0.5, 300, 0.25)&lt;br /&gt;    -1.71750854949725238788826203713e+578&lt;br /&gt;    &gt;&gt;&gt; mp.dps = 60; legenp(0.5, 300, 0.25)&lt;br /&gt;    -1.71750854949725238788826203712778687063419097363472262860567e+578&lt;br /&gt;    &gt;&gt;&gt; mp.dps = 15; legenp(0.5, 300, 0.25, hmag=300)   # faster, with manual perturbation&lt;br /&gt;    -1.71750854949725e+578&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Another example is for Bessel functions differentiated to high order. With the old code:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;    &gt;&gt;&gt; mp.dps = 15; besseli(2, 10, derivative=100)   # bad&lt;br /&gt;    2.63560662943646e+34&lt;br /&gt;    &gt;&gt;&gt; mp.dps = 30; besseli(2, 10, derivative=100)   # bad&lt;br /&gt;    23408889310840499424.9813614712&lt;br /&gt;    &gt;&gt;&gt; mp.dps = 60; besseli(2, 10, derivative=100)   # only accurate to a few digits&lt;br /&gt;    821.238621006501815018537509753672810563116338269219460709828&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;With the new code:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;    &gt;&gt;&gt; mp.dps = 15; besseli(2, 10, derivative=100)&lt;br /&gt;    821.238621006483&lt;br /&gt;    &gt;&gt;&gt; mp.dps = 30; besseli(2, 10, derivative=100)&lt;br /&gt;    821.238621006483348660925541651&lt;br /&gt;    &gt;&gt;&gt; mp.dps = 60; besseli(2, 10, derivative=100)&lt;br /&gt;    821.238621006483348660925541650744338655411830854860813048862&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;In related news, I've given all hypergeometric-type functions the ability to accept kwargs and pass them on to &lt;tt&gt;hypercomb&lt;/tt&gt; and &lt;tt&gt;hypsum&lt;/tt&gt;. This means that tuning and error control options will be available for a large number of functions (Bessel functions, etc), which could be useful for some users who need to do advanced calculations. I have yet to document these features thoroughly (the interface will also perhaps be tweaked before the next release).&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4754734402679928849-5515174066457792351?l=fredrik-j.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://fredrik-j.blogspot.com/feeds/5515174066457792351/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=4754734402679928849&amp;postID=5515174066457792351' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4754734402679928849/posts/default/5515174066457792351'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4754734402679928849/posts/default/5515174066457792351'/><link rel='alternate' type='text/html' href='http://fredrik-j.blogspot.com/2010/01/accurate-hypergeometric-functions-for.html' title='Accurate hypergeometric functions for large parameters'/><author><name>Fredrik Johansson</name><uri>http://www.blogger.com/profile/01465400860530971858</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4754734402679928849.post-2876311437327355449</id><published>2009-12-22T17:01:00.008+01:00</published><updated>2009-12-22T18:51:48.046+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='sage'/><category scheme='http://www.blogger.com/atom/ns#' term='sympy'/><category scheme='http://www.blogger.com/atom/ns#' term='mpmath'/><title type='text'>Analytic continuation of 3F2, 4F3 and higher functions</title><content type='html'>As of a recent &lt;a href="http://code.google.com/p/mpmath/source/detail?r=1001"&gt;commit&lt;/a&gt;, mpmath can evaluate the analytic continuation of the generalized hypergeometric function &lt;sub&gt;p+1&lt;/sub&gt;F&lt;sub&gt;p&lt;/sub&gt; for any p. Previously &lt;sub&gt;2&lt;/sub&gt;F&lt;sub&gt;1&lt;/sub&gt; (the Gaussian hypergeometric function) was supported -- see earlier blog posts -- but not &lt;sub&gt;3&lt;/sub&gt;F&lt;sub&gt;2&lt;/sub&gt; and higher. This addition means that the generalized hypergeometric function is finally supported essentially everywhere where it is "well-posed" (in the sense that the series has nonzero radius of convergence), so it is a rather significant improvement. Unfortunately, the implementation is still not perfect, but I decided to commit the existing code since it is quite useful already (and long overdue).&lt;br /&gt;&lt;br /&gt;As proof of operation, I deliver plots of &lt;sub&gt;3&lt;/sub&gt;F&lt;sub&gt;2&lt;/sub&gt; and &lt;sub&gt;4&lt;/sub&gt;F&lt;sub&gt;3&lt;/sub&gt;, requiring both |z| &amp;lt; 1 and |z| &gt; 1:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;from mpmath import *&lt;br /&gt;f1 = lambda z: hyp3f2(1,2,3,4,5,z)&lt;br /&gt;f2 = lambda z: hyper([1,2,3,4],[5,6,7],z)&lt;br /&gt;plot([f1,f2], [-2,2])&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_rh0QblLk0C0/SzD6bjFSbEI/AAAAAAAAALk/BUgMNZr-EeI/s1600-h/hyp34.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 400px; height: 302px;" src="http://3.bp.blogspot.com/_rh0QblLk0C0/SzD6bjFSbEI/AAAAAAAAALk/BUgMNZr-EeI/s400/hyp34.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5418105702907538498" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;A portrait of &lt;sub&gt;3&lt;/sub&gt;F&lt;sub&gt;2&lt;/sub&gt; restricted to the unit circle:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;plot(lambda x: hyp3f2(1,2,3,4,5,exp(2*pi*j*x)), [-1,1])&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://1.bp.blogspot.com/_rh0QblLk0C0/SzD8AnHQ0uI/AAAAAAAAALs/R034hmELfT0/s1600-h/hcircle.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 400px; height: 302px;" src="http://1.bp.blogspot.com/_rh0QblLk0C0/SzD8AnHQ0uI/AAAAAAAAALs/R034hmELfT0/s400/hcircle.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5418107439156351714" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;A numerical value of &lt;sub&gt;5&lt;/sub&gt;F&lt;sub&gt;4&lt;/sub&gt; with z on the unit circle, with general complex parameters:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;&gt;&gt;&gt; mp.dps = 50&lt;br /&gt;&gt;&gt;&gt; print hyper([1+j,0.5,-2j,1,0.5+0.5j],[0.5j,0.25,-j,-1-j],-1)&lt;br /&gt;(-1.8419705729324526212110109087877199070037836117341 - &lt;br /&gt;0.57799141426978758652682670969879368618437786161612j)&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;High precision values of &lt;sub&gt;3&lt;/sub&gt;F&lt;sub&gt;2&lt;/sub&gt; at z = 1 and z = 1 + &amp;epsilon;:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;&gt;&gt;&gt; print hyp3f2(1,1,2,3.5,1.5,1)&lt;br /&gt;2.2603241503205748814116375624327119385797950825522&lt;br /&gt;&gt;&gt;&gt; print hyp3f2(1,1,2,3.5,1.5,'1.0001')&lt;br /&gt;(2.2626812356987790952469291649495098300894035980837 -&lt;br /&gt;0.00092505569713084902188662960467216683326695892509251j)&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;A complex plot of &lt;sub&gt;3&lt;/sub&gt;F&lt;sub&gt;2&lt;/sub&gt;:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;&gt;&gt;&gt; mp.dps = 5&lt;br /&gt;&gt;&gt;&gt; cplot(lambda z: hyp3f2(2.5,3,4,1,2.25,z), [-2,2], [-2,2],&lt;br /&gt;...     points=50000)&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_rh0QblLk0C0/SzEG9q5FxaI/AAAAAAAAAL0/hfg6OZ2QHJA/s1600-h/hcplot.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 400px; height: 302px;" src="http://3.bp.blogspot.com/_rh0QblLk0C0/SzEG9q5FxaI/AAAAAAAAAL0/hfg6OZ2QHJA/s400/hcplot.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5418119483258946978" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;A bit of theoretical background: the hypergeometric series &lt;sub&gt;p&lt;/sub&gt;F&lt;sub&gt;q&lt;/sub&gt; has an infinite radius of convergence when p &amp;le; q, so it can in principle be evaluated then by adding sufficiently many terms at sufficiently high precision (although in practice asymptotic expansions must be used for large arguments, as mpmath does). In the balanced case when p = q+1, i.e. for &lt;sub&gt;1&lt;/sub&gt;F&lt;sub&gt;0&lt;/sub&gt;(a,z), &lt;sub&gt;2&lt;/sub&gt;F&lt;sub&gt;1&lt;/sub&gt;(a,b,c,z), &lt;sub&gt;3&lt;/sub&gt;F&lt;sub&gt;2&lt;/sub&gt;(...), &lt;sub&gt;4&lt;/sub&gt;F&lt;sub&gt;3&lt;/sub&gt;(...), ..., the series converges only when |z| &lt; 1 (plus in some special instances). This is due to the fact that the hypergeometric function has a singularity (a pole or branch point, depending on parameters) at z = 1, in turn owing to the fact that the hypergeometric differential equation is singular at z = 1. (The reason that the p = q+1 and not p = q case is "balanced" is that there is an extra, implicit factorial in the hypergeometric series.)&lt;br /&gt;&lt;br /&gt;The main ingredient of the analytic continuation of &lt;sub&gt;p+1&lt;/sub&gt;F&lt;sub&gt;p&lt;/sub&gt; is the &lt;a href="http://functions.wolfram.com/HypergeometricFunctions/HypergeometricPFQ/06/01/05/02/0004/"&gt;inversion formula&lt;/a&gt; which replaces z with 1/z and thus handles |z| &gt; 1. This was easy to implement -- the only complication is that integer parameters result in singular gamma factors, but the mechanisms to handle those automatically were already in place. There was no particular reason why I hadn't added that code already.&lt;br /&gt;&lt;br /&gt;The tricky part is the unit circle, and the close vicinity thereof, where neither series converges (quickly). I've been looking for good ways handle this, with mixed results.&lt;br /&gt;&lt;br /&gt;When I posed the problem on this blog several months back, a reader suggested  &lt;a href="http://www.jstor.org/pss/2159793"&gt;this paper by Wolfgang Bühring&lt;/a&gt; which gives a series for the analytic continuation around z = 1. My finding from trying to implement it is that the rate of convergence of the series generally is poor, and therefore it is not immediately effective for computation. However, convergence acceleration with &lt;tt&gt;nsum&lt;/tt&gt; improves the situation considerably in many cases. Some parameter combinations render the convergence acceleration useless, but even then, it can give a few correct digits, so it is better than nothing (although the implementation should probably warn the user when the result probably is inaccurate). I'm unfortunately not aware of any parameter transformations that would substantially improve convergence. The current implementation uses this method for &lt;sub&gt;3&lt;/sub&gt;F&lt;sub&gt;2&lt;/sub&gt; when |z-1| is small; it should work for 4F3 and higher too, but the series coefficients are much more complicated (involving multiply nested sums), so that's yet to be done.&lt;br /&gt;&lt;br /&gt;For the rest of the unit circle, I've settled for simply using convergence acceleration directly. This essentially just amounts to passing the hypergeometric series to &lt;tt&gt;nsum&lt;/tt&gt;, which applies Richardson extrapolation and iterated Shanks transformations. The Shanks transformation is actually perfect for this -- it's almost tailor made for convergence acceleration of the balanced hypergeometric series -- and is able to sum the series even outside the circle of convergence, using just a few terms. This covers most of the unit circle -- the catch is just that the acceleration asymptotically deteriorates and ultimately becomes useless close to z = 1, so complementary method (such as the Bühring's is still required there).&lt;br /&gt;&lt;br /&gt;Mathematica seems to support unit-circle evaluation of hypergeometric functions quite well. Unfortunately, I don't know how it does it. According to Wolfram's &lt;a href="http://reference.wolfram.com/mathematica/note/SomeNotesOnInternalImplementation.html"&gt;Some Notes On Internal Implementation&lt;/a&gt; page,&lt;br /&gt;&lt;br /&gt;&lt;blockquote&gt;&lt;br /&gt;The hypergeometric functions use functional equations, stable recurrence relations, series expansions, asymptotic series and Padé approximants. Methods from NSum and NIntegrate are also sometimes used. &lt;br /&gt;&lt;/blockquote&gt;&lt;br /&gt;&lt;br /&gt;This looks similar to what I'm doing -- high order Padé approximants and Mathematica's &lt;tt&gt;NSum&lt;/tt&gt; should be equivalent to the &lt;tt&gt;nsum&lt;/tt&gt;-based series acceleration in mpmath. But probably Mathematica employs some more tricks as well.&lt;br /&gt;&lt;br /&gt;I've also tested direct integration of the hypergeometric differential equation and of &lt;a href="http://mathworld.wolfram.com/Mellin-BarnesIntegral.html"&gt;Mellin-Barnes integral&lt;/a&gt; representations, but these approaches don't seem to work much better (at least not without many improvments), and at best seem to be relatively slow.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4754734402679928849-2876311437327355449?l=fredrik-j.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://fredrik-j.blogspot.com/feeds/2876311437327355449/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=4754734402679928849&amp;postID=2876311437327355449' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4754734402679928849/posts/default/2876311437327355449'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4754734402679928849/posts/default/2876311437327355449'/><link rel='alternate' type='text/html' href='http://fredrik-j.blogspot.com/2009/12/analytic-continuation-of-3f2-4f3-and.html' title='Analytic continuation of 3F2, 4F3 and higher functions'/><author><name>Fredrik Johansson</name><uri>http://www.blogger.com/profile/01465400860530971858</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://3.bp.blogspot.com/_rh0QblLk0C0/SzD6bjFSbEI/AAAAAAAAALk/BUgMNZr-EeI/s72-c/hyp34.png' height='72' width='72'/><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4754734402679928849.post-228740447542509597</id><published>2009-09-10T19:29:00.007+02:00</published><updated>2009-09-10T22:14:34.069+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='sage'/><category scheme='http://www.blogger.com/atom/ns#' term='sympy'/><category scheme='http://www.blogger.com/atom/ns#' term='mpmath'/><title type='text'>Python floats and other unusual things spotted in mpmath</title><content type='html'>I've just put up a &lt;a href="http://code.google.com/p/mpmath/source/browse/#svn/branches/mp4/mp4"&gt;branch&lt;/a&gt; (named "mp4") containing changes to mpmath that I've been working on for several days now. This branch includes some rather large changes, including support for fixed-precision (machine-precision) arithmetic, and a new implementation of the multiprecision type. The code is a bit rough at the moment, and not all changes are final, so don't expect this in a release in the immediate future.&lt;br /&gt;&lt;br /&gt;&lt;h4&gt;New mpf implementation&lt;/h4&gt;&lt;br /&gt;The mp4 branch uses a new &lt;tt&gt;mpf&lt;/tt&gt; type that handles both real and complex values. It makes complex arithmetic quite a bit faster (up to twice the speed), although real arithmetic is a little slower. Some of the slowdown is due to wrapping old-format functions instead of providing new ones, so there is a bit of unnecessary overhead. The biggest advantage is not that the implementation becomes faster, but that it becomes simpler. For example, a nice feature is that complex calculations that yield real results don't need to be converted back to a real type manually. Unfortunately, writing code that also supports Python float and complex (see below) somewhat reduce the usefulness of such features.&lt;br /&gt;&lt;br /&gt;Imaginary infinities are a little more well-behaved (i.e. &lt;tt&gt;j*(j*inf)&lt;/tt&gt; gives &lt;tt&gt;-inf&lt;/tt&gt; and not a complex NaN), and the representation is flexible enough that it might be possible to support unsigned infinities, infinities with an arbitrary complex sign, and possibly even zero with an arbitrary complex sign -- hey, perhaps infinities and zeros with order and residue information attached to them so you can evaluate &lt;tt&gt;gamma(-3)**2 / gamma(-5) / gamma(-2)&lt;/tt&gt; directly? (Well, I'm not sure if this is actually possible, and if it is, it's probably way overkill :-)&lt;br /&gt;&lt;br /&gt;This new implementation will not necessarily make it -- it depends on whether the pros outweigh the cons. I would very much appreciate review by others. Regardless of the outcome, writing it has been very useful for identifying and fixing problems in the interface between "low level" and "high level" code in mpmath.&lt;br /&gt;&lt;br /&gt;This should particularly simplify the future support for a C or Cython-based backend. In fact, the current fixed-precision backend (see below) only uses about 200 lines of code -- and although it's not complete yet, it handles a large set of calculations. Adding a fast multiprecision backend based on Sage or GMPY can probably be done in an evening now. I'm not going to do such a thing right now, as I want to fix up the existing code before adding anything new; should someone else be interested though, then by all means go ahead.&lt;br /&gt;&lt;br /&gt;I've also inserted a few other optimizations. As noted before on this blog, many of the special functions in mpmath are evaluated as linear combinations of hypergeometric series. All those functions basically fall back to a single routine which sums hypergeometric series. The mp4 branch contains a new implementation of this routine that is up to about twice as fast as before. The speed is achieved by code generation: for every different "type" (degree, parameter types) of hypergeometric series, a specialized version is generated with various bookkeeping done in advance so it doesn't have to be repeated in every iteration of the inner loop.&lt;br /&gt;&lt;br /&gt;As an example, the following runs at 1.7x the speed:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;&gt;&gt;&gt; 1/timing(mpmath.hyp1f1, 1.5, 2.2, 1.35)&lt;br /&gt;5620.8844813722862&lt;br /&gt;&gt;&gt;&gt; 1/timing(mp4.hyp1f1, 1.5, 2.2, 1.35)&lt;br /&gt;9653.1737629459149&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Nearly all tests pass with the new &lt;tt&gt;mpf&lt;/tt&gt; type used instead of the old one -- the only significant missing piece is interval arithmetic.&lt;br /&gt;&lt;br /&gt;&lt;h4&gt;Fixed-precision arithmetic&lt;/h4&gt;&lt;br /&gt;&lt;br /&gt;Several people have requested support for working with regular Python floats and complexes in mpmath. Often you only need a few digits of accuracy, and mpmath's multiprecision arithmetic is unnecessarily slow. Many (though not all) of the algorithms in mpmath work well in fixed precision; the main problem with supporting this feature has been that of providing an appropriate interface that avoids cluttering the code.&lt;br /&gt;&lt;br /&gt;In the mp4 branch, this is solved by adding a fixed-precision context. This context uses the same "high-level" methods for special functions, calculus, linear algebra, etc, as the multiprecision context, while low-level functions are replaced with fixed-precision versions. For example &lt;tt&gt;exp&lt;/tt&gt; is just a wrapper around &lt;tt&gt;math.exp&lt;/tt&gt; and &lt;tt&gt;cmath.exp&lt;/tt&gt;.&lt;br /&gt;&lt;br /&gt;While the default multiprecision context instance is called &lt;tt&gt;mp&lt;/tt&gt;, the default fixed-precision context instance is called &lt;tt&gt;fp&lt;/tt&gt;. So:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;&gt;&gt;&gt; from mp4 import mp, fp&lt;br /&gt;&gt;&gt;&gt; mp.pretty = True&lt;br /&gt;&gt;&gt;&gt; mp.sqrt(-5); type(_)&lt;br /&gt;2.23606797749979j&lt;br /&gt;&amp;lt;class 'mp4.ctx_mp.mpf'&gt;&lt;br /&gt;&gt;&gt;&gt; fp.sqrt(-5); type(_)&lt;br /&gt;2.2360679774997898j&lt;br /&gt;&amp;lt;type 'complex'&gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;The fixed-precision context is still missing a lot of low-level functions, so many things don't work yet. Let's try a couple of calculations that do work and see how they compare.&lt;br /&gt;&lt;br /&gt;&lt;h5&gt;Lambert W function&lt;/h5&gt;&lt;br /&gt;&lt;br /&gt;The Lambert W function was the first nontrivial function I tried to get working, since it's very useful in fixed precision and its calculation only requires simple functions. The &lt;tt&gt;lambertw&lt;/tt&gt; method is the same for &lt;tt&gt;mp&lt;/tt&gt; as for &lt;tt&gt;fp&lt;/tt&gt;; the latter just uses &lt;tt&gt;float&lt;/tt&gt; or &lt;tt&gt;complex&lt;/tt&gt; for the arithmetic.&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;&gt;&gt;&gt; mp.lambertw(7.5)&lt;br /&gt;1.56623095378239&lt;br /&gt;&gt;&gt;&gt; mp.lambertw(3+4j)&lt;br /&gt;(1.28156180612378 + 0.533095222020971j)&lt;br /&gt;&gt;&gt;&gt; fp.lambertw(7.5)&lt;br /&gt;1.5662309537823875&lt;br /&gt;&gt;&gt;&gt; fp.lambertw(3+4j)&lt;br /&gt;(1.2815618061237759+0.53309522202097104j)&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;The fixed-precision results are very accurate, which is not surprising since the Lambert W function is implemented using a "self-correcting" convergent iteration. In fact, the multiprecision implementation could be sped up by using the fixed-precision version to generate the initial value. The speed difference is quite striking:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;&gt;&gt;&gt; 1/timing(mp.lambertw, 7.5)&lt;br /&gt;1249.5319808144905&lt;br /&gt;&gt;&gt;&gt; 1/timing(mp.lambertw, 3+4j)&lt;br /&gt;603.49697841726618&lt;br /&gt;&gt;&gt;&gt; 1/timing(fp.lambertw, 7.5)&lt;br /&gt;36095.559380378654&lt;br /&gt;&gt;&gt;&gt; 1/timing(fp.lambertw, 3+4j)&lt;br /&gt;20281.934235976787&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Both the real and complex versions are about &lt;b&gt;30x faster&lt;/b&gt; in fixed precision.&lt;br /&gt;&lt;br /&gt;&lt;h5&gt;Hurwitz zeta function&lt;/h5&gt;&lt;br /&gt;&lt;br /&gt;The Hurwitz zeta function is implemented mainly using Euler-Maclaurin summation. The main ingredients are Bernoulli numbers and the powers in the truncated L-series. Bernoulli numbers are cached, and can be computed relatively quickly to begin with, so they're not much to worry about. In fact, I implemented fixed-precision Bernoulli numbers by wrapping the arbitrary-precision routine for them, so they are available to full 53-bit accuracy. As it turns out, the fixed-precision evaluation achieves nearly full accuracy:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;&gt;&gt;&gt; mp.hurwitz(3.5, 2.25); fp.hurwitz(3.5, 2.25)&lt;br /&gt;0.0890122424923889&lt;br /&gt;0.089012242492388816&lt;br /&gt;&gt;&gt;&gt; t1=timing(mp.hurwitz,3.5,2.25); t2=timing(fp.hurwitz,3.5,2.25); 1/t1; 1/t2; t1/t2&lt;br /&gt;473.66504799548278&lt;br /&gt;7008.0267335004173&lt;br /&gt;14.7953216374269&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;There is a nice 15x speedup, and it gets even better if we try complex values. Let's evaluate the the zeta function on the critical line 0.5+&lt;i&gt;t&lt;/i&gt;i for increasing values of &lt;i&gt;t&lt;/i&gt;:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;&gt;&gt;&gt; s=0.5+10j&lt;br /&gt;&gt;&gt;&gt; mp.hurwitz(s); fp.hurwitz(s)&lt;br /&gt;(1.54489522029675 - 0.115336465271273j)&lt;br /&gt;(1.5448952202967554-0.11533646527127067j)&lt;br /&gt;&gt;&gt;&gt; t1=timing(mp.hurwitz,s); t2=timing(fp.hurwitz,s); 1/t1; 1/t2; t1/t2&lt;br /&gt;213.37891598750548&lt;br /&gt;4391.4815202596583&lt;br /&gt;20.580672180923461&lt;br /&gt;&lt;br /&gt;&gt;&gt;&gt; s=0.5+1000j&lt;br /&gt;&gt;&gt;&gt; mp.hurwitz(s); fp.hurwitz(s)&lt;br /&gt;(0.356334367194396 + 0.931997831232994j)&lt;br /&gt;(0.35633436719476846+0.93199783123336344j)&lt;br /&gt;&gt;&gt;&gt; t1=timing(mp.hurwitz,s); t2=timing(fp.hurwitz,s); 1/t1; 1/t2; t1/t2&lt;br /&gt;8.1703453931669383&lt;br /&gt;352.54843617352128&lt;br /&gt;43.149759185011469&lt;br /&gt;&lt;br /&gt;&gt;&gt;&gt; s = 0.5+100000j&lt;br /&gt;&gt;&gt;&gt; mp.hurwitz(s); fp.hurwitz(s)&lt;br /&gt;(1.07303201485775 + 5.7808485443635j)&lt;br /&gt;(1.0730320148426455+5.7808485443942352j)&lt;br /&gt;&gt;&gt;&gt; t1=timing(mp.hurwitz,s); t2=timing(fp.hurwitz,s); 1/t1; 1/t2; t1/t2&lt;br /&gt;0.17125208455924443&lt;br /&gt;9.2754935956407891&lt;br /&gt;54.162806949260492&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;It's definitely possible to go up to slightly larger heights still. The Euler-Maclaurin truncation in the Hurwitz zeta implementation is not really tuned, and certainly has not been tuned for fixed precision, so the speed can probably be improved.&lt;br /&gt;&lt;br /&gt;&lt;h5&gt;Hypergeometric functions&lt;/h5&gt;&lt;br /&gt;&lt;br /&gt;Implementing hypergeometric series in fixed precision was trivial. That the multi-precision implementation is actually quite fast can be seen by comparing direct evaluations:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;&gt;&gt;&gt; mp.hyp1f1(2.5, 1.2, -4.5)&lt;br /&gt;-0.0164674858506064&lt;br /&gt;&gt;&gt;&gt; fp.hyp1f1(2.5, 1.2, -4.5)&lt;br /&gt;-0.016467485850591584&lt;br /&gt;&gt;&gt;&gt; 1/timing(mp.hyp1f1, 2.5, 1.2, -4.5)&lt;br /&gt;6707.6667199744124&lt;br /&gt;&gt;&gt;&gt; 1/timing(fp.hyp1f1, 2.5, 1.2, -4.5)&lt;br /&gt;12049.135305946565&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;The float version is only about twice as fast. Unfortunately, hypergeometric series suffer from catastrophic cancellation in fixed precision, as can be seen by trying a larger argument:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;&gt;&gt;&gt; mp.hyp1f1(2.5, 1.2, -30.5)&lt;br /&gt;6.62762709628679e-5&lt;br /&gt;&gt;&gt;&gt; fp.hyp1f1(2.5, 1.2, -30.5)&lt;br /&gt;-0.012819333651375751&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Potentially, checks could be added so that the fixed-precision series raises an exception or falls back to arbitrary-precision arithmetic internally when catastrophic cancellation occurs. However, it turns out that this evaluation works for even larger arguments when the numerically stable asymptotic series kicks in:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;&gt;&gt;&gt; mp.hyp1f1(2.5, 1.2, -300.5)&lt;br /&gt;1.79669541078302e-7&lt;br /&gt;&gt;&gt;&gt; fp.hyp1f1(2.5, 1.2, -300.5+0j)&lt;br /&gt;1.7966954107830195e-07&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;The reason I used a complex argument is that the asymptotic series uses complex arithmetic internally, and Python has an annoying habit of raising exceptions when performing complex-valued operations involving floats (a proper workaround will have to be added). In this case the speedup is close to an order of magnitude:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;&gt;&gt;&gt; 1/timing(mp.hyp1f1, 2.5, 1.2, -300.5)&lt;br /&gt;532.36666412814452&lt;br /&gt;&gt;&gt;&gt; 1/timing(fp.hyp1f1, 2.5, 1.2, -300.5+0j)&lt;br /&gt;4629.4746136865342&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Another example, a Bessel function. This function is calculated using a hypergeometric series and also a couple of additional factors, so the speedup is quite large:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;&gt;&gt;&gt; mp.besselk(2.5, 7.5)&lt;br /&gt;0.000367862846522012&lt;br /&gt;&gt;&gt;&gt; fp.besselk(2.5, 7.5)&lt;br /&gt;0.00036786284652201188&lt;br /&gt;&gt;&gt;&gt; 1/timing(mp.besselk, 2.5, 7.5)&lt;br /&gt;3410.5578142787444&lt;br /&gt;&gt;&gt;&gt; 1/timing(fp.besselk, 2.5, 7.5)&lt;br /&gt;21879.520083463747&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;The fixed-precision context does not yet handle cancellation of singularities in hypergeometric functions. This can be implemented as in the multiprecision case by perturbing values, although accuracy will be quite low (at best 5-6 digits; sometimes an accurate result will be impossible to obtain).&lt;br /&gt;&lt;br /&gt;&lt;h5&gt;Numerical calculus&lt;/h5&gt;&lt;br /&gt;&lt;br /&gt;Root-finding works, as does numerical integration, with nice speedups:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;&gt;&gt;&gt; fp.quad(lambda x: fp.cos(x), [2, 5])&lt;br /&gt;-1.8682217014888205&lt;br /&gt;&gt;&gt;&gt; mp.quad(lambda x: mp.cos(x), [2, 5])&lt;br /&gt;-1.86822170148882&lt;br /&gt;&gt;&gt;&gt; 1/timing(fp.quad, lambda x: fp.cos(x), [2, 5])&lt;br /&gt;1368.3622602114056&lt;br /&gt;&gt;&gt;&gt; 1/timing(mp.quad, lambda x: mp.cos(x), [2, 5])&lt;br /&gt;55.480651962846274&lt;br /&gt;&lt;br /&gt;&gt;&gt;&gt; fp.findroot(lambda x: fp.cos(x), 2)&lt;br /&gt;1.5707963267948966&lt;br /&gt;&gt;&gt;&gt; mp.findroot(lambda x: mp.cos(x), 2)&lt;br /&gt;1.5707963267949&lt;br /&gt;&gt;&gt;&gt; 1/timing(fp.findroot, lambda x: fp.cos(x), 2)&lt;br /&gt;19160.822293284604&lt;br /&gt;&gt;&gt;&gt; 1/timing(mp.findroot, lambda x: mp.cos(x), 2)&lt;br /&gt;1039.4805452292442&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;The tests above use well-behaved object functions; some corner cases are likely fragile at this point. I also know, without having tried, that many other calculus functions utterly don't work in fixed precision (not by algorithm, nor by implementation). Some work will be needed to support them even partially. At minimum, several functions will have to be changed to use an epsilon of 10&lt;sup&gt;-5&lt;/sup&gt; or so since full 15-16-digit accuracy requires extra working precision which just isn't available.&lt;br /&gt;&lt;br /&gt;&lt;h5&gt;Plotting&lt;/h5&gt;&lt;br /&gt;&lt;br /&gt;The plot methods work, and are of course way faster in fixed-precision mode. For your enjoyment, here is the gamma function in high resolution; the following image took only a few seconds to generate:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;&gt;&gt;&gt; fp.cplot(fp.gamma, points=400000)&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_rh0QblLk0C0/SqlJejiSx1I/AAAAAAAAALQ/TB2m_CtxSzk/s1600-h/gamma.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 400px; height: 305px;" src="http://4.bp.blogspot.com/_rh0QblLk0C0/SqlJejiSx1I/AAAAAAAAALQ/TB2m_CtxSzk/s400/gamma.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5379912019154749266" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;With the default number of points (&lt;tt&gt;fp.cplot(fp.gamma)&lt;/tt&gt;), it's of course instantaneous.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4754734402679928849-228740447542509597?l=fredrik-j.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://fredrik-j.blogspot.com/feeds/228740447542509597/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=4754734402679928849&amp;postID=228740447542509597' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4754734402679928849/posts/default/228740447542509597'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4754734402679928849/posts/default/228740447542509597'/><link rel='alternate' type='text/html' href='http://fredrik-j.blogspot.com/2009/09/python-floats-and-other-unusual-things.html' title='Python floats and other unusual things spotted in mpmath'/><author><name>Fredrik Johansson</name><uri>http://www.blogger.com/profile/01465400860530971858</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://4.bp.blogspot.com/_rh0QblLk0C0/SqlJejiSx1I/AAAAAAAAALQ/TB2m_CtxSzk/s72-c/gamma.png' height='72' width='72'/><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4754734402679928849.post-5943058755620412002</id><published>2009-08-13T21:49:00.004+02:00</published><updated>2009-08-13T22:06:47.404+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='sage'/><category scheme='http://www.blogger.com/atom/ns#' term='sympy'/><category scheme='http://www.blogger.com/atom/ns#' term='mpmath'/><title type='text'>Released: mpmath 0.13</title><content type='html'>I released &lt;a href="http://code.google.com/p/mpmath/"&gt;mpmath&lt;/a&gt; 0.13 today. See the &lt;a href="http://groups.google.com/group/mpmath/browse_thread/thread/960b02f0a2ed5dfa"&gt;announcement&lt;/a&gt; for details. Soon coming to a Sage or SymPy near you!&lt;br /&gt;&lt;br /&gt;I've blogged extensively about the new features that went into this version over the last two months. The short version of the changelog is "lots of special functions". For convenience, here a list of all posts:&lt;br /&gt;&lt;br /&gt;&lt;ul&gt;&lt;br /&gt;&lt;li&gt;&lt;a href="http://fredrik-j.blogspot.com/2009/08/torture-testing-special-functions.html"&gt;Torture testing special functions&lt;/a&gt;&lt;/li&gt;&lt;br /&gt;&lt;li&gt;&lt;a href="http://fredrik-j.blogspot.com/2009/08/3d-visualization-of-complex-functions.html"&gt;3D visualization of complex functions with matplotlib&lt;/a&gt;&lt;/li&gt;&lt;br /&gt;&lt;li&gt;&lt;a href="http://fredrik-j.blogspot.com/2009/08/coulomb-wave-functions-and-orthogonal.html"&gt;Coulomb wave functions and orthogonal polynomials&lt;/a&gt;&lt;/li&gt;&lt;br /&gt;&lt;li&gt;&lt;a href="http://fredrik-j.blogspot.com/2009/07/hurwitz-zeta-function-dirichlet-l.html"&gt;Hurwitz zeta function, Dirichlet L-series, Appell F1&lt;/a&gt;&lt;/li&gt;&lt;br /&gt;&lt;li&gt;&lt;a href="http://fredrik-j.blogspot.com/2009/07/another-mathematica-bug.html"&gt;Another Mathematica bug&lt;/a&gt;&lt;/li&gt;&lt;br /&gt;&lt;li&gt;&lt;a href="http://fredrik-j.blogspot.com/2009/07/improved-incomplete-gamma-and.html"&gt;Improved incomplete gamma and exponential integrals; Clausen functions&lt;/a&gt;&lt;/li&gt;&lt;br /&gt;&lt;li&gt;&lt;a href="http://fredrik-j.blogspot.com/2009/06/meijer-g-more-hypergeometric-functions.html"&gt;Meijer G, more hypergeometric functions, fractional differentiation&lt;/a&gt;&lt;/li&gt;&lt;br /&gt;&lt;li&gt;&lt;a href="http://fredrik-j.blogspot.com/2009/06/massive-hypergeometric-update.html"&gt;Massive hypergeometric update&lt;/a&gt;&lt;/li&gt;&lt;br /&gt;&lt;li&gt;&lt;a href="http://fredrik-j.blogspot.com/2009/06/hypergeometric-2f1-incomplete-beta.html"&gt;Hypergeometric 2F1, incomplete beta, exponential integrals&lt;/a&gt;&lt;/li&gt;&lt;br /&gt;&lt;/ul&gt;&lt;br /&gt;&lt;br /&gt;I also suggest a look at the &lt;a href="http://mpmath.googlecode.com/svn/tags/0.13/doc/build/index.html"&gt;documentation&lt;/a&gt; and particularly the &lt;a href="http://mpmath.googlecode.com/svn/tags/0.13/doc/build/functions/index.html"&gt;special functions&lt;/a&gt; section.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4754734402679928849-5943058755620412002?l=fredrik-j.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://fredrik-j.blogspot.com/feeds/5943058755620412002/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=4754734402679928849&amp;postID=5943058755620412002' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4754734402679928849/posts/default/5943058755620412002'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4754734402679928849/posts/default/5943058755620412002'/><link rel='alternate' type='text/html' href='http://fredrik-j.blogspot.com/2009/08/released-mpmath-013.html' title='Released: mpmath 0.13'/><author><name>Fredrik Johansson</name><uri>http://www.blogger.com/profile/01465400860530971858</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4754734402679928849.post-2543446893077772693</id><published>2009-08-11T02:27:00.007+02:00</published><updated>2009-08-11T16:28:42.289+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='sage'/><category scheme='http://www.blogger.com/atom/ns#' term='sympy'/><category scheme='http://www.blogger.com/atom/ns#' term='mpmath'/><title type='text'>Torture testing special functions</title><content type='html'>Today I &lt;a href="http://code.google.com/p/mpmath/source/detail?r=962"&gt;implemented&lt;/a&gt; a set of torture test for special functions in mpmath.&lt;br /&gt;&lt;br /&gt;The function torturer takes a function, say &lt;tt&gt;f = airyai&lt;/tt&gt; or &lt;tt&gt;f = lambda z: hyp1f1(1.5, -2.25, z)&lt;/tt&gt;, and attempts to evaluate it with z = a &amp;middot; 10&lt;sup&gt;&lt;i&gt;n&lt;/i&gt;&lt;/sup&gt; as &lt;i&gt;n&lt;/i&gt; ranges between -20 and 20, with &lt;i&gt;a&lt;/i&gt; &amp;asymp; 1, 1+i, i, -1+i, -1. (The "&amp;asymp;" indicates multiplication by an additional quasi-random factor near unity.)&lt;br /&gt;&lt;br /&gt;Thus it tests a wide range of magnitudes (both tiny and huge), with pure positive, negative, imaginary, and complex arguments; i.e. covering most distinct regions of the complex plane. (It doesn't cover the lower half-plane, but that's generally just a waste of time since most function algorithms are agnostic about the sign of the imaginary part -- this should be done for some functions in the future, however.)&lt;br /&gt;&lt;br /&gt;Each evaluation is also performed at a range of precisions, between 5 and 150 digits by default (for fast functions the max precision is set much higher). The results at two successive precisions are compared to verify that they agree relatively to the lesser of the precisions (with a single-digit error allowed for roundoff error).&lt;br /&gt;&lt;br /&gt;This doesn't guarantee correctness, of course -- I might have implemented the wrong formula for the function -- but it's a strong check that whatever formula has been implemented is being evaluated accurately. It does provide some amount of correctness testing for those functions that use more than one algorithm depending on precision and/or the magnitude of the argument (and this includes the hypergeometric functions). If one algorithm is used at low precision and another at high precision, then an error in either will be revealed when comparing results.&lt;br /&gt;&lt;br /&gt;My original intention with was just to check robustness of the asymptotic expansions of hypergeometric functions. Testing with a continuous range of magnitudes ensures that there aren't any regions where convergence gets impossibly slow, a loop fails to terminate, an error estimate is insufficient, etc. (And this needs to be done at several different levels of precision.) I then ended up including many other functions in the list of torture tests as well.&lt;br /&gt;&lt;br /&gt;The results? I discovered (and fixed!) some 10-20 bugs in mpmath. Most were of the kind  "only 43 accurate digits were returned where 50 were expected" and due to inadequate error estimates and/or insufficient extra precision being allocated (mostly in argument transformations). I actually knew about many of the problems, and had just neglected to fix them because they wouldn't show up for "normal" arguments (and in particular, there weren't any existing tests that failed).&lt;br /&gt;&lt;br /&gt;Lesson to be learned: if it isn't tested, it probably doesn't work (fully). Semi-automatic exhaustive or randomized testing (the more "evil" inputs, the better), is a necessary complement to testing hand-picked values.&lt;br /&gt;&lt;br /&gt;There is more to be done:&lt;br /&gt;&lt;br /&gt;&lt;ul&gt;&lt;br /&gt;&lt;li&gt;Some functions don't yet pass the torture tests&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Rather than just large/small, the relevant stress measure for some functions is closeness to a negative integer, closeness to the unit circle, etc&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Functions of more than one argument should be tested with randomly selected values for all parameters&lt;/li&gt;&lt;br /&gt;&lt;/ul&gt;&lt;br /&gt;&lt;br /&gt;But already now, the torture tests have done a good job. Tellingly, functions I implemented recently are much less buggy than functions I implemented long ago (even just three months ago). Periodically rewriting old code as one learns more is bound to improve quality :-)&lt;br /&gt;&lt;br /&gt;Speaking of bugs, Mathematica doesn't pass the mpmath torture test suite (far from it). An example of something that will fail:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;In[47]:= N[HypergeometricPFQ[{1},{-1/4,1/3},10^10],15]&lt;br /&gt;&lt;br /&gt;General::ovfl: Overflow occurred in computation.&lt;br /&gt;&lt;br /&gt;General::ovfl: Overflow occurred in computation.&lt;br /&gt;&lt;br /&gt;General::ovfl: Overflow occurred in computation.&lt;br /&gt;&lt;br /&gt;General::stop: Further output of General::ovfl&lt;br /&gt;     will be suppressed during this calculation.&lt;br /&gt;&lt;br /&gt;(... nothing for 5 minutes)&lt;br /&gt;&lt;br /&gt;Interrupt&gt; a&lt;br /&gt;&lt;br /&gt;Out[47]= $Aborted&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;And mpmath (instantaneously):&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;&gt;&gt;&gt; mp.dps = 15; mp.pretty = True&lt;br /&gt;&gt;&gt;&gt; hyp1f2(1,(-1,4),(1,3),10**10)&lt;br /&gt;-3.53521222026601e+86866&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;By the way, the current set of torture tests takes about 11 minutes to run on my computer with psyco and gmpy (much longer without either). It's possibly a better benchmark than the standard tests.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Update&lt;/b&gt;: running the torture tests with &lt;a href="http://docs.python.org/library/multiprocessing.html"&gt;multiprocessing&lt;/a&gt; on sage.math takes only 28 seconds (which is the time for the slowest individual test). Go parallel!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4754734402679928849-2543446893077772693?l=fredrik-j.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://fredrik-j.blogspot.com/feeds/2543446893077772693/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=4754734402679928849&amp;postID=2543446893077772693' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4754734402679928849/posts/default/2543446893077772693'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4754734402679928849/posts/default/2543446893077772693'/><link rel='alternate' type='text/html' href='http://fredrik-j.blogspot.com/2009/08/torture-testing-special-functions.html' title='Torture testing special functions'/><author><name>Fredrik Johansson</name><uri>http://www.blogger.com/profile/01465400860530971858</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4754734402679928849.post-4524847327993438716</id><published>2009-08-09T19:33:00.011+02:00</published><updated>2009-08-09T22:23:37.242+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='sage'/><category scheme='http://www.blogger.com/atom/ns#' term='sympy'/><category scheme='http://www.blogger.com/atom/ns#' term='mpmath'/><category scheme='http://www.blogger.com/atom/ns#' term='matplotlib'/><title type='text'>3D visualization of complex functions with matplotlib</title><content type='html'>Hooray! &lt;a href="http://matplotlib.sourceforge.net/users/whats_new.html"&gt;matplotlib 0.99 is out and it has 3D plotting&lt;/a&gt;, finally!&lt;br /&gt;&lt;br /&gt;I've shown a lot of color plots of complex functions on this blog to demonstrate complex functions in mpmath. These plots are informative, but sometimes a 3D plot (typically of the function's absolute value) gives a much better view. A big advantage of 3D plots over 2D color plots is that far fewer evaluation points are required for a good high-resolution image, and this helps when visualizing the slower functions in mpmath.&lt;br /&gt;&lt;br /&gt;There will probably be a 3D plot function in a future version of mpmath (or two functions; for two-variable real, and complex functions), similar in style to the existing matplotlib wrappers &lt;tt&gt;plot&lt;/tt&gt; and &lt;tt&gt;cplot&lt;/tt&gt;. Until I've figured out the details, I'll share a couple of test plots.&lt;br /&gt;&lt;br /&gt;Coulomb wave function of a complex argument, F(6,4,z):&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://2.bp.blogspot.com/_rh0QblLk0C0/Sn8U5zO9mjI/AAAAAAAAAKg/Tvj0JE20BL8/s1600-h/coulomb_wireframe.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 400px; height: 302px;" src="http://2.bp.blogspot.com/_rh0QblLk0C0/Sn8U5zO9mjI/AAAAAAAAAKg/Tvj0JE20BL8/s400/coulomb_wireframe.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5368032264086985266" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Principal-branch logarithmic gamma function:&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_rh0QblLk0C0/Sn8U1tMMxfI/AAAAAAAAAKY/r9YdXNms00E/s1600-h/loggamma_wireframe.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 400px; height: 302px;" src="http://4.bp.blogspot.com/_rh0QblLk0C0/Sn8U1tMMxfI/AAAAAAAAAKY/r9YdXNms00E/s400/loggamma_wireframe.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5368032193745307122" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Gamma function:&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_rh0QblLk0C0/Sn8UyCi-gVI/AAAAAAAAAKQ/YHdt_sVD57s/s1600-h/gamma_wireframe.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 400px; height: 302px;" src="http://4.bp.blogspot.com/_rh0QblLk0C0/Sn8UyCi-gVI/AAAAAAAAAKQ/YHdt_sVD57s/s400/gamma_wireframe.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5368032130758508882" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Imaginary part of Lambert W function, 0th branch:&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://1.bp.blogspot.com/_rh0QblLk0C0/Sn8ZFhPtnVI/AAAAAAAAAKo/jNLzYhiM2IA/s1600-h/lambertw_imag.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 400px; height: 302px;" src="http://1.bp.blogspot.com/_rh0QblLk0C0/Sn8ZFhPtnVI/AAAAAAAAAKo/jNLzYhiM2IA/s400/lambertw_imag.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5368036863463234898" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Riemann zeta function in the critical strip:&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://2.bp.blogspot.com/_rh0QblLk0C0/Sn8KjPAMrFI/AAAAAAAAAKA/F0GsOx1sLF4/s1600-h/zeta_3d.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 400px; height: 185px;" src="http://2.bp.blogspot.com/_rh0QblLk0C0/Sn8KjPAMrFI/AAAAAAAAAKA/F0GsOx1sLF4/s400/zeta_3d.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5368020881287982162" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Those are all wireframe plots. It's also possible to do surface plots. Using the Riemann zeta function again, a surface plot looks as follows:&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_rh0QblLk0C0/Sn8KjDdCZ-I/AAAAAAAAAJ4/0irD_S2lKR4/s1600-h/zeta_3d_2.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 400px; height: 232px;" src="http://4.bp.blogspot.com/_rh0QblLk0C0/Sn8KjDdCZ-I/AAAAAAAAAJ4/0irD_S2lKR4/s400/zeta_3d_2.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5368020878187718626" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Surface + wireframe plot:&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://1.bp.blogspot.com/_rh0QblLk0C0/Sn8Ki72ArXI/AAAAAAAAAJw/xJosMvUdImI/s1600-h/zeta_3d_3.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 400px; height: 207px;" src="http://1.bp.blogspot.com/_rh0QblLk0C0/Sn8Ki72ArXI/AAAAAAAAAJw/xJosMvUdImI/s400/zeta_3d_3.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5368020876144979314" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;None of these is perfect. I'd like to be able to do a surface plot with a widely spaced wireframe mesh to pronounce the geometry, and smooth colored surface between the meshes. This doesn't seem possible with matplotlib because the surface plot doesn't do smoothing (even with a stride selected); overlaying a wireframe works decently, but the wireframe doesn't render with occlusion and this looks bad for some functions. Since the pure wireframe plot is much faster, I think I prefer it for now.&lt;br /&gt;&lt;br /&gt;For complex functions, it'd also be nice with a color function separate from the geometry function -- then you could plot the phase as color in the same picture.&lt;br /&gt;&lt;br /&gt;Color helps for visualizing complicated structure, especially for phase plots. Below, I've plotted the phase (actually the sine of the phase, to make it continuous) of a Jacobi theta function &amp;theta;&lt;sub&gt;3&lt;/sub&gt;(1+4j/3,q) (restricted to |q| &lt; 0.8, because it gets extremely messy for larger |q|):&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_rh0QblLk0C0/Sn8juvpY36I/AAAAAAAAALA/BjM9kv2CTdQ/s1600-h/thetaphase.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 400px; height: 302px;" src="http://4.bp.blogspot.com/_rh0QblLk0C0/Sn8juvpY36I/AAAAAAAAALA/BjM9kv2CTdQ/s400/thetaphase.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5368048566819938210" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;The phase of the Barnes G-function:&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://2.bp.blogspot.com/_rh0QblLk0C0/Sn8lSkT6RsI/AAAAAAAAALI/Ik6CspngEvE/s1600-h/barnesgphase.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 400px; height: 302px;" src="http://2.bp.blogspot.com/_rh0QblLk0C0/Sn8lSkT6RsI/AAAAAAAAALI/Ik6CspngEvE/s400/barnesgphase.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5368050281763980994" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;I could do many more, but that will probably enough for this blog :-)&lt;br /&gt;&lt;br /&gt;To reproduce any of these, use the following script with trivial modifications. It's a straightforward extension of the example scripts in the matplotlib documentation.&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;from mpl_toolkits.mplot3d import Axes3D&lt;br /&gt;from matplotlib import cm&lt;br /&gt;import pylab&lt;br /&gt;import numpy as np&lt;br /&gt;import mpmath&lt;br /&gt;mpmath.dps = 5&lt;br /&gt;&lt;br /&gt;# Use instead of arg for a continuous phase&lt;br /&gt;def arg2(x):&lt;br /&gt;    return mpmath.sin(mpmath.arg(x))&lt;br /&gt;&lt;br /&gt;#f = lambda z: abs(mpmath.loggamma(z))&lt;br /&gt;#f = lambda z: arg2(mpmath.exp(z))&lt;br /&gt;#f = lambda z: abs(mpmath.besselj(3,z))&lt;br /&gt;f = lambda z: arg2(mpmath.cos(z))&lt;br /&gt;&lt;br /&gt;fig = pylab.figure()&lt;br /&gt;ax = Axes3D(fig)&lt;br /&gt;X = np.arange(-5, 5, 0.125)&lt;br /&gt;Y = np.arange(-5, 5, 0.125)&lt;br /&gt;X, Y = np.meshgrid(X, Y)&lt;br /&gt;xn, yn = X.shape&lt;br /&gt;W = X*0&lt;br /&gt;for xk in range(xn):&lt;br /&gt;    for yk in range(yn):&lt;br /&gt;        try:&lt;br /&gt;            z = complex(X[xk,yk],Y[xk,yk])&lt;br /&gt;            w = float(f(z))&lt;br /&gt;            if w != w:&lt;br /&gt;                raise ValueError&lt;br /&gt;            W[xk,yk] = w&lt;br /&gt;        except (ValueError, TypeError, ZeroDivisionError):&lt;br /&gt;            # can handle special values here&lt;br /&gt;            pass&lt;br /&gt;    print xk, xn&lt;br /&gt;&lt;br /&gt;# can comment out one of these&lt;br /&gt;ax.plot_surface(X, Y, W, rstride=1, cstride=1, cmap=cm.jet)&lt;br /&gt;ax.plot_wireframe(X, Y, W, rstride=5, cstride=5)&lt;br /&gt;&lt;br /&gt;pylab.show()&lt;br /&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4754734402679928849-4524847327993438716?l=fredrik-j.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://fredrik-j.blogspot.com/feeds/4524847327993438716/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=4754734402679928849&amp;postID=4524847327993438716' title='8 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4754734402679928849/posts/default/4524847327993438716'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4754734402679928849/posts/default/4524847327993438716'/><link rel='alternate' type='text/html' href='http://fredrik-j.blogspot.com/2009/08/3d-visualization-of-complex-functions.html' title='3D visualization of complex functions with matplotlib'/><author><name>Fredrik Johansson</name><uri>http://www.blogger.com/profile/01465400860530971858</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://2.bp.blogspot.com/_rh0QblLk0C0/Sn8U5zO9mjI/AAAAAAAAAKg/Tvj0JE20BL8/s72-c/coulomb_wireframe.png' height='72' width='72'/><thr:total>8</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4754734402679928849.post-7001930780064203944</id><published>2009-08-04T22:38:00.008+02:00</published><updated>2009-08-05T00:58:01.944+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='sage'/><category scheme='http://www.blogger.com/atom/ns#' term='sympy'/><category scheme='http://www.blogger.com/atom/ns#' term='mpmath'/><title type='text'>Coulomb wave functions and orthogonal polynomials</title><content type='html'>Work has been a bit slow over the last two weeks, partially due to the fact that I was away during one of them. Nevertheless, I've been able to add a couple of more functions to mpmath, as described in the following post below. With these functions committed, I'm probably going to stop adding features and just do a new release as soon as possible (some documentation and general cleanup will be required first).&lt;br /&gt;&lt;br /&gt;&lt;h4&gt;Coulomb wave functions&lt;/h4&gt; I have, due to a request, implemented the &lt;a href="http://mathworld.wolfram.com/CoulombWaveFunction.html"&gt;Coulomb wave functions&lt;/a&gt; (&lt;a href="http://code.google.com/p/mpmath/source/detail?r=957"&gt;commit&lt;/a&gt;). The Coulomb wave functions are used in quantum physics; they solve the radial part of the Schrödinger equation for a particle in a 1/&lt;i&gt;r&lt;/i&gt; potential.&lt;br /&gt;&lt;br /&gt;The versions in mpmath are fully general. They work not only for an arbitrarily large or small radius, but for complex values of all parameters. Some example evaluations:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;&gt;&gt;&gt; mp.dps = 25; mp.pretty = True&lt;br /&gt;&gt;&gt;&gt; coulombf(4, 2.5, 1000000)&lt;br /&gt;-0.9713831935409625197404938&lt;br /&gt;&gt;&gt;&gt; coulombf(2+3j, 1+j, 100j)&lt;br /&gt;(2.161557009297068729111076e+37 + 1.217455850269101085622437e+39j)&lt;br /&gt;&gt;&gt;&gt; coulombg(-3.5, 2, '1e-100')&lt;br /&gt;2.746460651456730226435998e+252&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;The definitions of the Coulomb wave functions for complex parameters are based mostly on &lt;a href="http://arxiv.org/abs/physics/0702051v1"&gt;this paper by N. Michel&lt;/a&gt;, which describes a special-purpose C++ implementation. I'm not aware of any other software that implements Coulomb wave functions, except for GSL which only supports limited domains in fixed precision.&lt;br /&gt;&lt;br /&gt;The Coulomb wave functions are numerically difficult to calculate: the canonical representation requires complex arithmetic, and the terms vary by many orders of magnitude even for small parameter values. Arbitrary-precision arithmetic, therefore, is almost necessary even to obtain low-precision values, unless one uses much more specialized evaluation methods. The current mpmath versions are simple and robust, although they can be fairly slow in worst cases (i.e. when they need to use a high internal precision). Timings for average/good cases are in the millisecond range,&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;&gt;&gt;&gt; timing(coulombf, 3, 2, 10)&lt;br /&gt;0.0012244852348553437&lt;br /&gt;&gt;&gt;&gt; timing(coulombg, 3, 2, 10)&lt;br /&gt;0.0068572681723514609&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;but they can climb up towards a second or so when large cancellations occur. Someone who needs faster Coulomb wave functions, say for a limited range of parameters, could perhaps find mpmath useful for testing a more specialized implementation against.&lt;br /&gt;&lt;br /&gt;The speed is plenty for basic visualization purposes. Below I've recreated graphs 14.1, 14.2 and 14.5 from &lt;a href="http://www.iopb.res.in/~somen/abramowitz_and_stegun/page_539.htm"&gt;Abramowitz &amp; Stegun&lt;/a&gt;. &lt;table&gt;&lt;br /&gt;&lt;tr&gt;&lt;br /&gt;&lt;td&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_rh0QblLk0C0/SnidtRYXmhI/AAAAAAAAAJI/kaj2yKbUhzY/s1600-h/14_1.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 200px; height: 151px;" src="http://3.bp.blogspot.com/_rh0QblLk0C0/SnidtRYXmhI/AAAAAAAAAJI/kaj2yKbUhzY/s200/14_1.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5366212357097232914" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;/td&gt;&lt;br /&gt;&lt;td&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://2.bp.blogspot.com/_rh0QblLk0C0/SniduHxbVOI/AAAAAAAAAJQ/Dc37tM37DX8/s1600-h/14_2.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 200px; height: 151px;" src="http://2.bp.blogspot.com/_rh0QblLk0C0/SniduHxbVOI/AAAAAAAAAJQ/Dc37tM37DX8/s200/14_2.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5366212371697849570" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;/td&gt;&lt;br /&gt;&lt;td&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://2.bp.blogspot.com/_rh0QblLk0C0/SniduhFJG9I/AAAAAAAAAJY/vuv5DtXgsJY/s1600-h/14_5.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 200px; height: 151px;" src="http://2.bp.blogspot.com/_rh0QblLk0C0/SniduhFJG9I/AAAAAAAAAJY/vuv5DtXgsJY/s200/14_5.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5366212378491427794" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;/td&gt;&lt;br /&gt;&lt;/tr&gt;&lt;br /&gt;&lt;/table&gt;&lt;pre&gt;mp.dps = 5&lt;br /&gt;&lt;br /&gt;# Figure 14.1&lt;br /&gt;F1 = lambda x: coulombf(x,1,10)&lt;br /&gt;F2 = lambda x: coulombg(x,1,10)&lt;br /&gt;plot([F1,F2], [0,15], [-1.2, 1.7])&lt;br /&gt;&lt;br /&gt;# Figure 14.3&lt;br /&gt;F1 = lambda x: coulombf(0,0,x)&lt;br /&gt;F2 = lambda x: coulombf(0,1,x)&lt;br /&gt;F3 = lambda x: coulombf(0,5,x)&lt;br /&gt;F4 = lambda x: coulombf(0,10,x)&lt;br /&gt;F5 = lambda x: coulombf(0,x/2,x)&lt;br /&gt;plot([F1,F2,F3,F4,F5], [0,25], [-1.2,1.6])&lt;br /&gt;&lt;br /&gt;# Figure 14.5&lt;br /&gt;F1 = lambda x: coulombg(0,0,x)&lt;br /&gt;F2 = lambda x: coulombg(0,1,x)&lt;br /&gt;F3 = lambda x: coulombg(0,5,x)&lt;br /&gt;F4 = lambda x: coulombg(0,10,x)&lt;br /&gt;F5 = lambda x: coulombg(0,x/2,x)&lt;br /&gt;plot([F1,F2,F3,F4,F5], [0,30], [-2,2])&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Also, a plot for complex values, &lt;tt&gt;cplot(lambda z: coulombg(1+j, -0.5, z))&lt;/tt&gt;:&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://2.bp.blogspot.com/_rh0QblLk0C0/Snie9UIuNoI/AAAAAAAAAJg/K8l4atn3H3U/s1600-h/cplx.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 400px; height: 302px;" src="http://2.bp.blogspot.com/_rh0QblLk0C0/Snie9UIuNoI/AAAAAAAAAJg/K8l4atn3H3U/s400/cplx.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5366213732226446978" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;h4&gt;Orthogonal polynomials&lt;/h4&gt; I've added the (associated) Legendre functions of the first and second kind, Gegenbauer, (associated) Laguerre and Hermite functions (commits &lt;a href="http://code.google.com/p/mpmath/source/detail?r=958"&gt;1&lt;/a&gt;, &lt;a href="http://code.google.com/p/mpmath/source/detail?r=959"&gt;2&lt;/a&gt;, &lt;a href="http://code.google.com/p/mpmath/source/detail?r=960"&gt;3&lt;/a&gt;, &lt;a href="http://code.google.com/p/mpmath/source/detail?r=961"&gt;4&lt;/a&gt;). This means that mpmath finally supports all the classical orthogonal polynomials. As usual, they work in generalized form, for complex values of all parameters.&lt;br /&gt;&lt;br /&gt;A fun thing to do is to verify orthogonality of the orthogonal polynomials using numerical quadrature: &lt;pre&gt;&lt;br /&gt;&gt;&gt;&gt; mp.dps = 15&lt;br /&gt;&gt;&gt;&gt; chop(quad(lambda t: exp(-t)*t**2*laguerre(3,2,t)*laguerre(4,2,t), [0,inf]))&lt;br /&gt;0.0&lt;br /&gt;&gt;&gt;&gt; chop(quad(lambda t: exp(-t**2)*hermite(3,t)*hermite(4,t), [-inf,inf]))&lt;br /&gt;0.0&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;As another basic demonstration, here are some Legendre functions of the second kind, visualized: &lt;pre&gt;F1 = lambda x: legenq(0,0,x)&lt;br /&gt;F2 = lambda x: legenq(1,0,x)&lt;br /&gt;F3 = lambda x: legenq(2,0,x)&lt;br /&gt;F4 = lambda x: legenq(3,0,x)&lt;br /&gt;F5 = lambda x: legenq(4,0,x)&lt;br /&gt;plot([F1,F2,F3,F4,F5], [-1,1], [-1.3,1.3])&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_rh0QblLk0C0/SnitzMZlGBI/AAAAAAAAAJo/IuqtoNOboO0/s1600-h/legenq.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 400px; height: 302px;" src="http://4.bp.blogspot.com/_rh0QblLk0C0/SnitzMZlGBI/AAAAAAAAAJo/IuqtoNOboO0/s400/legenq.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5366230051025393682" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Implementing these functions is a lot of work, I've found. The general case is simple (just fall back to generic hypergeometric code), but the special cases (singularities, limits, asymptotically large arguments) are easy to get wrong and there are lots of them to test and document.&lt;br /&gt;&lt;br /&gt;My general methodology is to implement the special functions as the &lt;a href="http://functions.wolfram.com/"&gt;Wolfram Functions site&lt;/a&gt; defines them (if they are listed there), test my implementation against exact formulas, and then test numerical values against Mathematica. Unfortunately, Wolfram Functions often leaves limits undefined, and is not always consistent with Mathematica. In fact, Mathematica is not even consistent with itself. Consider the following:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;In[277]:= LaguerreL[-1, b, z] // FunctionExpand&lt;br /&gt;&lt;br /&gt;Out[277]= 0&lt;br /&gt;&lt;br /&gt;In[278]:= LaguerreL[-1, -2, z] // FunctionExpand&lt;br /&gt;&lt;br /&gt;           z  2&lt;br /&gt;          E  z&lt;br /&gt;Out[278]= -----&lt;br /&gt;            2&lt;br /&gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;It's tempting to just leave such a case undefined in mpmath and move on (and perhaps fix it later if it turns out to be used). Ideally mpmath should handle all singular cases correctly and consistently, and it should state in the documentation precisely what it will calculate for any given input so that the user doesn't have to guess. Testing and documenting special cases is very time-consuming, however, and although I'm making progress, this work is far from complete.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4754734402679928849-7001930780064203944?l=fredrik-j.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://fredrik-j.blogspot.com/feeds/7001930780064203944/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=4754734402679928849&amp;postID=7001930780064203944' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4754734402679928849/posts/default/7001930780064203944'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4754734402679928849/posts/default/7001930780064203944'/><link rel='alternate' type='text/html' href='http://fredrik-j.blogspot.com/2009/08/coulomb-wave-functions-and-orthogonal.html' title='Coulomb wave functions and orthogonal polynomials'/><author><name>Fredrik Johansson</name><uri>http://www.blogger.com/profile/01465400860530971858</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://3.bp.blogspot.com/_rh0QblLk0C0/SnidtRYXmhI/AAAAAAAAAJI/kaj2yKbUhzY/s72-c/14_1.png' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4754734402679928849.post-1848526875357439940</id><published>2009-07-15T00:02:00.013+02:00</published><updated>2009-07-15T04:43:26.612+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='sage'/><category scheme='http://www.blogger.com/atom/ns#' term='sympy'/><category scheme='http://www.blogger.com/atom/ns#' term='mpmath'/><title type='text'>Hurwitz zeta function, Dirichlet L-series, Appell F1</title><content type='html'>I've added three more functions to mpmath since the last blog update: the Hurwitz zeta function, Dirichlet L-series and the Appell hypergeometric function.&lt;br /&gt;&lt;br /&gt;&lt;h4&gt;Hurwitz zeta function&lt;/h4&gt;&lt;br /&gt;The &lt;a href="http://en.wikipedia.org/wiki/Hurwitz_zeta_function"&gt;Hurwitz zeta function&lt;/a&gt; is available with the syntax &lt;tt&gt;hurwitz(s, a=1, derivative=0)&lt;/tt&gt;. It's a separate function from &lt;tt&gt;zeta&lt;/tt&gt; for various reasons (one being to make the plain zeta function as simple as possible). With the optional third argument, it not only computes values, but arbitrarily high derivatives with respect to &lt;i&gt;s&lt;/i&gt; (more on which later).&lt;br /&gt;&lt;br /&gt;It's quite fast at high precision; e.g. the following evaluation takes just 0.25 seconds (0.6 seconds from cold cache) on my laptop:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;&gt;&gt;&gt; mp.dps = 1000&lt;br /&gt;&gt;&gt;&gt; hurwitz(3, (1,3))        # (1,3) specifies the exact parameter 1/3&lt;br /&gt;27.56106119970080377622787797740750928454209531301488108286446031855695818017193&lt;br /&gt;31971447814492522557242833928839896347960163079006102651117997856881812399406784&lt;br /&gt;05731893349475372409101251211346682545314192272823745384665266049022078198043979&lt;br /&gt;99746337871597509019667252833751410875882452970087673701166818534638337151352084&lt;br /&gt;77353765613742227179887954515352387981214398169985388076469409432688912355506092&lt;br /&gt;00433349370785407936365607196038965500743780377965599471761097326271922616743812&lt;br /&gt;30503180321801486524844073873393780084553765789276561564723000726671702263215399&lt;br /&gt;29351207878018547946361756911396554507918933344824138599697851666075594487957278&lt;br /&gt;34823450478752821675163024463415647205608959739100071026336912826930683261155867&lt;br /&gt;06265136825191025873785920661492516925137079914816949085911457084632783280741306&lt;br /&gt;98897458344746469272828267484989756595259917290800848768074029766417786815732919&lt;br /&gt;07917349747600320626141004448429987450854391175868138793015691343081417209625034&lt;br /&gt;88644344900903488585429308918210445984048&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;With s = &amp;pi; instead, it takes 3.5 seconds. For comparison, Mathematica 6 took 0.5 and 8.3 seconds respectively (on an unloaded remote system which is faster than my laptop, but I'm not going to guess by how much).&lt;br /&gt;&lt;br /&gt;The function is a bit slow at low precision, relatively speaking, but fast enough (in the 1-10 millisecond range) for plotting and such. Of course, it works for complex &lt;i&gt;s&lt;/i&gt;, and in particular on the critical line 1/2+it. Below, I've plotted |&amp;zeta;(1/2+i&lt;i&gt;t&lt;/i&gt;,1)|&lt;sup&gt;2&lt;/sup&gt;, |&amp;zeta;(1/2+i&lt;i&gt;t&lt;/i&gt;,1/2)|&lt;sup&gt;2&lt;/sup&gt;, |&amp;zeta;(1/2+i&lt;i&gt;t&lt;/i&gt;,1/3)|&lt;sup&gt;2&lt;/sup&gt;; the first image shows 0 &amp;le; &lt;i&gt;t&lt;/i&gt; &lt; 30 and the second is with 1000 &amp;le; &lt;i&gt;t&lt;/i&gt; &lt; 1030.&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://1.bp.blogspot.com/_rh0QblLk0C0/Sl0AqhwbtyI/AAAAAAAAAIQ/W7q_vE7f2WA/s1600-h/crit_1.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 400px; height: 132px;" src="http://1.bp.blogspot.com/_rh0QblLk0C0/Sl0AqhwbtyI/AAAAAAAAAIQ/W7q_vE7f2WA/s400/crit_1.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5358439862256776994" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://1.bp.blogspot.com/_rh0QblLk0C0/Sl0AqzaO5tI/AAAAAAAAAIY/Rh0OT0vtZlE/s1600-h/crit_3.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 400px; height: 132px;" src="http://1.bp.blogspot.com/_rh0QblLk0C0/Sl0AqzaO5tI/AAAAAAAAAIY/Rh0OT0vtZlE/s400/crit_3.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5358439866995500754" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;To show some more complex values, here are plots of &amp;zeta;(s, 1), &amp;zeta;(s, 1/3) and &amp;zeta;(s, 24/25) for 100 &amp;le; Im(&lt;i&gt;s&lt;/i&gt;) &amp;le; 110. I picked the range [-2, 3] for the real part to show that the reflection formula for Re(&lt;i&gt;s&lt;/i&gt;) &lt; 0 works:&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_rh0QblLk0C0/Sl0yBDl8ngI/AAAAAAAAAI4/PXlmAy4wJms/s1600-h/strip_composite3.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 400px; height: 228px;" src="http://4.bp.blogspot.com/_rh0QblLk0C0/Sl0yBDl8ngI/AAAAAAAAAI4/PXlmAy4wJms/s400/strip_composite3.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5358494125366484482" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;In fact, the Hurwitz zeta implementation works for complex values of both &lt;i&gt;s&lt;/i&gt; and &lt;i&gt;a&lt;/i&gt;. Here is a plot of &lt;tt&gt;hurwitz(3+4j, a)&lt;/tt&gt; with respect to &lt;i&gt;a&lt;/i&gt;:&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://2.bp.blogspot.com/_rh0QblLk0C0/Sl0IhW4huPI/AAAAAAAAAIg/ZsqB8_wmKAg/s1600-h/hurwitz_34.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 400px; height: 389px;" src="http://2.bp.blogspot.com/_rh0QblLk0C0/Sl0IhW4huPI/AAAAAAAAAIg/ZsqB8_wmKAg/s400/hurwitz_34.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5358448500812134642" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;The evaluation can be slow and/or inaccurate for nonrational values of &lt;i&gt;a&lt;/i&gt;, though (in nonconvergent cases where the functional equation isn't used).&lt;br /&gt;&lt;br /&gt;&lt;h4&gt;Zeta function performance&lt;/h4&gt;&lt;br /&gt;Right now the implementations of the Riemann zeta function and the Hurwitz zeta function in mpmath are entirely separate. In fact, they even use entirely different algorithms (&lt;tt&gt;hurwitz&lt;/tt&gt; uses Euler-Maclaurin summation, while &lt;tt&gt;zeta&lt;/tt&gt; uses the Borwein approximation). This is useful for verifying correctness of either function.&lt;br /&gt;&lt;br /&gt;Regarding performance, it appears that the Borwein approximation is slightly faster for small |Im(s)| while Euler-Maclaurin is massively faster for large |s|.&lt;br /&gt;&lt;br /&gt;The following benchmark might give an idea:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;&gt;&gt;&gt; from mpmath import *&lt;br /&gt;&gt;&gt;&gt; mp.dps = 15; mp.pretty = True&lt;br /&gt;&gt;&gt;&gt; timing(zeta, 0.5+10j); timing(hurwitz, 0.5+10j)&lt;br /&gt;0.0027386162207766627&lt;br /&gt;0.0082901877193889192&lt;br /&gt;&gt;&gt;&gt; timing(zeta, 0.5+100j); timing(hurwitz, 0.5+100j)&lt;br /&gt;0.0075319349246901089&lt;br /&gt;0.041037198861866478&lt;br /&gt;&gt;&gt;&gt; timing(zeta, 0.5+1000j); timing(hurwitz, 0.5+1000j)&lt;br /&gt;0.13468499488063479&lt;br /&gt;0.18062463246027072&lt;br /&gt;&gt;&gt;&gt; timing(zeta, 0.5+10000j); timing(hurwitz, 0.5+10000j)&lt;br /&gt;7.2977593520964241&lt;br /&gt;1.0488757649366072&lt;br /&gt;&gt;&gt;&gt; timing(zeta, 0.5+10000j); timing(hurwitz, 0.5+10000j)&lt;br /&gt;0.84306636737350971&lt;br /&gt;1.0601629536714867&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;As the last evaluation shows, the Borwein algorithm is still not doing too poorly at 0.5+10000j from a warm cache, but at this point the cache is 50 MB large. At 0.5+100000j my laptop ran out of RAM and Firefox crashed (fortunately Blogger autosaves!) so it clearly doesn't scale. The Euler-Maclaurin algorithm caches a lot of Bernoulli numbers, but the memory consumption of this appears to be negligible. With &lt;tt&gt;hurwitz&lt;/tt&gt; (and the function value, for those interested),&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;&gt;&gt;&gt; timing(hurwitz, 0.5+100000j)&lt;br /&gt;8.0361576680835149&lt;br /&gt;&gt;&gt;&gt; hurwitz(0.5+100000j)&lt;br /&gt;(1.07303201485775 + 5.7808485443635j)&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;and the memory usage of the Python interpreter process only climbs to 12 MB.&lt;br /&gt;&lt;br /&gt;Just by optimizing the pure Python code, I think the Hurwitz zeta function (at low precision) could be improved by about a factor 2 generally and perhaps by a factor 3-4 on the critical line (where square roots can be used instead of logarithms -- &lt;tt&gt;zeta&lt;/tt&gt; uses this optimization but &lt;tt&gt;hurwitz&lt;/tt&gt; doesn't yet); with Cython, perhaps by as much as a factor 10.&lt;br /&gt;&lt;br /&gt;The real way to performance is not to use arbitrary-precision arithmetic, though. Euler-Maclaurin summation for zeta functions is remarkably stable in fixed-precision arithmetic, so there is no problem using doubles for most applications. As I wrote a while back on sage-devel, a preliminary version of my Hurwitz zeta code for Python &lt;tt&gt;complex&lt;/tt&gt; was 5x faster than Sage's CDF &lt;tt&gt;zeta&lt;/tt&gt; (in a single test, mind you). If there is interest, I could add such a version, perhaps writing it in Cython for Sage (for even greater speed).&lt;br /&gt;&lt;br /&gt;&lt;h4&gt;Derivatives&lt;/h4&gt;&lt;br /&gt;The Hurwitz zeta function can be differentiated easily like so:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;&gt;&gt;&gt; mp.dps = 25&lt;br /&gt;&gt;&gt;&gt; hurwitz(2+3j, 1.25, 3)&lt;br /&gt;(-0.01266157985314340398338543 - 0.06298953579777517606036907j)&lt;br /&gt;&gt;&gt;&gt; diff(lambda s: hurwitz(s, 1.25), 2+3j, 3)&lt;br /&gt;(-0.01266157985314340398338543 - 0.06298953579777517606036907j)&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;For simple applications one can just as well use the numerical &lt;tt&gt;diff&lt;/tt&gt; function as above with reasonable performance, but this isn't really feasible at high precision and/or high orders (numerically computing an &lt;i&gt;n&lt;/i&gt;th derivative at precision &lt;i&gt;p&lt;/i&gt; requires &lt;i&gt;n&lt;/i&gt;+1 function evaluations, each at precision (&lt;i&gt;n&lt;/i&gt;+1)&amp;times;&lt;i&gt;p&lt;/i&gt;).&lt;br /&gt;&lt;br /&gt;The specialized code in &lt;tt&gt;hurwitz&lt;/tt&gt; requires no extra precision (although it still needs to perform extra work) and therefore scales up to very high precision and high orders. Here for example is a derivative of order 100 (taking 0.5 seconds) and one of order 200 (taking 4 seconds):&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;&gt;&gt;&gt; hurwitz(2+3j, 1.25, 100)&lt;br /&gt;(2.604086240825183469410664e+107 - 1.388710675207202633247271e+107j)&lt;br /&gt;&gt;&gt;&gt; hurwitz(2+3j, 1.25, 200)&lt;br /&gt;(2.404124816484789309285653e+274 + 6.633220818921777955999455e+273j)&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;It should be possible to calculate a sequence of derivatives much more quickly than with separate calls, although this isn't implemented yet. One use for this is to produce high-degree Taylor series. This could potentially be used in a future version of mpmath, for example to provide very fast moderate-precision (up to 50 digits, say) function for multi-evaluation of the Riemann zeta function on the real line or not too far away from the real line. This in turn would speed up other functions, such as the prime zeta function and perhaps polylogarithms in some cases, which are computed using series over many Riemann zeta values.&lt;br /&gt;&lt;br /&gt;&lt;h4&gt;Dirichlet L-series&lt;/h4&gt;&lt;br /&gt;With the Hurwitz zeta function in place, it was a piece of cake to also supply an evaluation routine for arbitrary &lt;a href="http://en.wikipedia.org/wiki/Dirichlet_L-function"&gt;Dirichlet L-series&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;The way it works it that you provide the character explicitly as a list (so it also works for other periodic sequences than Dirichlet characters), and it evaluates it as a sum of Hurwitz zeta values.&lt;br /&gt;&lt;br /&gt;For example (copypasting from the docstring), the following defines and verifies some values of the &lt;a href="http://mathworld.wolfram.com/DirichletBetaFunction.html"&gt;Dirichlet beta function&lt;/a&gt;, which is defined by a Dirichlet character modulo 4:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;&gt;&gt;&gt; B = lambda s, d=0: dirichlet(s, [0, 1, 0, -1], d)&lt;br /&gt;&gt;&gt;&gt; B(0); 1./2&lt;br /&gt;0.5&lt;br /&gt;0.5&lt;br /&gt;&gt;&gt;&gt; B(1); pi/4&lt;br /&gt;0.7853981633974483096156609&lt;br /&gt;0.7853981633974483096156609&lt;br /&gt;&gt;&gt;&gt; B(2); +catalan&lt;br /&gt;0.9159655941772190150546035&lt;br /&gt;0.9159655941772190150546035&lt;br /&gt;&gt;&gt;&gt; B(2,1); diff(B, 2)&lt;br /&gt;0.08158073611659279510291217&lt;br /&gt;0.08158073611659279510291217&lt;br /&gt;&gt;&gt;&gt; B(-1,1); 2*catalan/pi&lt;br /&gt;0.5831218080616375602767689&lt;br /&gt;0.5831218080616375602767689&lt;br /&gt;&gt;&gt;&gt; B(0,1); log(gamma(0.25)**2/(2*pi*sqrt(2)))&lt;br /&gt;0.3915943927068367764719453&lt;br /&gt;0.3915943927068367764719454&lt;br /&gt;&gt;&gt;&gt; B(1,1); 0.25*pi*(euler+2*ln2+3*ln(pi)-4*ln(gamma(0.25)))&lt;br /&gt;0.1929013167969124293631898&lt;br /&gt;0.1929013167969124293631898&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;h4&gt;Appell hypergeometric function&lt;/h4&gt;&lt;br /&gt;The function &lt;tt&gt;appellf1&lt;/tt&gt; computes the &lt;a href="http://en.wikipedia.org/wiki/Appell_series"&gt;Appell F1 function&lt;/a&gt; which is a hypergeometric series in two variables. I made the implementation reasonably fast by rewriting it as a single series in &lt;tt&gt;hyp2f1&lt;/tt&gt; (with the side effect of supporting the analytic continuation with that for 2F1).&lt;br /&gt;&lt;br /&gt;A useful feature of the Appell F1 function is that it provides a closed form for some integrals, including those of the form&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://2.bp.blogspot.com/_rh0QblLk0C0/Sl076x7TE4I/AAAAAAAAAJA/K5jgc8CbBDE/s1600-h/integr.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 174px; height: 21px;" src="http://2.bp.blogspot.com/_rh0QblLk0C0/Sl076x7TE4I/AAAAAAAAAJA/K5jgc8CbBDE/s400/integr.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5358505012661261186" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;with arbitrary parameter values, for arbitrary endpoints (and that's a quite general integral indeed). Comparing with numerical quadrature:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;&gt;&gt;&gt; def integral(a,b,p,q,r,x1,x2):&lt;br /&gt;...     a,b,p,q,r,x1,x2 = map(mpmathify, [a,b,p,q,r,x1,x2])&lt;br /&gt;...     f = lambda x: x**r * (x+a)**p * (x+b)**q&lt;br /&gt;...     def F(x):&lt;br /&gt;...         v = x**(r+1)/(r+1) * (a+x)**p * (b+x)**q&lt;br /&gt;...         v *= (1+x/a)**(-p)&lt;br /&gt;...         v *= (1+x/b)**(-q)&lt;br /&gt;...         v *= appellf1(r+1,-p,-q,2+r,-x/a,-x/b)&lt;br /&gt;...         return v&lt;br /&gt;...     print "Num. quad:", quad(f, [x1,x2])&lt;br /&gt;...     print "Appell F1:", F(x2)-F(x1)&lt;br /&gt;...&lt;br /&gt;&gt;&gt;&gt; integral('1/5','4/3','-2','3','1/2',0,1)&lt;br /&gt;Num. quad: 9.073335358785776206576981&lt;br /&gt;Appell F1: 9.073335358785776206576981&lt;br /&gt;&gt;&gt;&gt; integral('3/2','4/3','-2','3','1/2',0,1)&lt;br /&gt;Num. quad: 1.092829171999626454344678&lt;br /&gt;Appell F1: 1.092829171999626454344678&lt;br /&gt;&gt;&gt;&gt; integral('3/2','4/3','-2','3','1/2',12,25)&lt;br /&gt;Num. quad: 1106.323225040235116498927&lt;br /&gt;Appell F1: 1106.323225040235116498927&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Also incomplete elliptic integrals are covered, so you can for example define one like this:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;&gt;&gt;&gt; def E(z, m):&lt;br /&gt;...     if (pi/2).ae(z):&lt;br /&gt;...         return ellipe(m)&lt;br /&gt;...     return 2*round(re(z)/pi)*ellipe(m) + mpf(-1)**round(re(z)/pi)*\&lt;br /&gt;...         sin(z)*appellf1(0.5,0.5,-0.5,1.5,sin(z)**2,m*sin(z)**2)&lt;br /&gt;...&lt;br /&gt;&gt;&gt;&gt; z, m = 1, 0.5&lt;br /&gt;&gt;&gt;&gt; E(z,m); quad(lambda t: sqrt(1-m*sin(t)**2), [0,pi/4,3*pi/4,z])&lt;br /&gt;0.9273298836244400669659042&lt;br /&gt;0.9273298836244400669659042&lt;br /&gt;&gt;&gt;&gt; z, m = 3, 2&lt;br /&gt;&gt;&gt;&gt; E(z,m); quad(lambda t: sqrt(1-m*sin(t)**2), [0,pi/4,3*pi/4,z])&lt;br /&gt;(1.057495752337234229715836 + 1.198140234735592207439922j)&lt;br /&gt;(1.057495752337234229715836 + 1.198140234735592207439922j)&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;The Appell series isn't really faster than numerical quadrature, but it's possibly more robust (the singular points need to be given to &lt;tt&gt;quad&lt;/tt&gt; as above to obtain any accuracy, for example). Mpmath doesn't yet have incomplete elliptic integrals; the version above could be a start, although I'll probably want to try a more canonical approach.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4754734402679928849-1848526875357439940?l=fredrik-j.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://fredrik-j.blogspot.com/feeds/1848526875357439940/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=4754734402679928849&amp;postID=1848526875357439940' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4754734402679928849/posts/default/1848526875357439940'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4754734402679928849/posts/default/1848526875357439940'/><link rel='alternate' type='text/html' href='http://fredrik-j.blogspot.com/2009/07/hurwitz-zeta-function-dirichlet-l.html' title='Hurwitz zeta function, Dirichlet L-series, Appell F1'/><author><name>Fredrik Johansson</name><uri>http://www.blogger.com/profile/01465400860530971858</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://1.bp.blogspot.com/_rh0QblLk0C0/Sl0AqhwbtyI/AAAAAAAAAIQ/W7q_vE7f2WA/s72-c/crit_1.png' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4754734402679928849.post-2807753352375203858</id><published>2009-07-12T20:32:00.004+02:00</published><updated>2009-07-12T20:58:28.009+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='sage'/><category scheme='http://www.blogger.com/atom/ns#' term='sympy'/><category scheme='http://www.blogger.com/atom/ns#' term='mpmath'/><title type='text'>Another Mathematica bug</title><content type='html'>Mathematica is great for cross-checking numerical values, but it's not unusual to run into bugs, so triple checking is a good habit.&lt;br /&gt;&lt;br /&gt;Here is mpmath's evaluation for two Struve function values:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;&gt;&gt;&gt; struvel(1+j, 100j)&lt;br /&gt;(0.1745249349140313153158106 + 0.08029354364282519308755724j)&lt;br /&gt;&gt;&gt;&gt; struvel(1+j, 700j)&lt;br /&gt;(-0.1721150049480079451246076 + 0.1240770953126831093464055j)&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;The same values in Mathematica:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;In[52]:= N[StruveL[1+I, 100I], 25]&lt;br /&gt;Out[52]= 0.1745249349140313153158107 + 0.0802935436428251930875572 I&lt;br /&gt;&lt;br /&gt;In[53]:= N[StruveL[1+I, 700I], 25]&lt;br /&gt;Out[53]= -0.2056171312291138282112197 + 0.0509264284065420772723951 I&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;I'm almost certain that the second value returned by Mathematica is wrong. The value from mpmath agrees with a high-precision direct summation of the series defining the Struve L function, and even Mathematica gives the expected value if one rewrites the L function in terms of the H function:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;In[59]:= n=1+I; z=700I&lt;br /&gt;Out[59]= 700 I&lt;br /&gt;&lt;br /&gt;In[60]:= N[-I Exp[-n Pi I/2] StruveH[n, I z], 25]&lt;br /&gt;Out[60]= -0.1721150049480079451246076 + 0.1240770953126831093464055 I&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Maple also agrees with mpmath:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;&gt; evalf(StruveL(1+I, 700*I), 25);&lt;br /&gt;        -0.1721150049480079451246076 + 0.1240770953126831093464055 I&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;So unless Mathematica uses some nonstandard definition of Struve functions, unannounced, this very much looks like a bug in their implementation.&lt;br /&gt;&lt;br /&gt;Wolfram Alpha &lt;a href="http://www.wolframalpha.com/input/?i=StruveL[1%2BI%2C+700I]"&gt;reproduces&lt;/a&gt; the faulty value, so this still appears to be broken in Mathematica 7.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4754734402679928849-2807753352375203858?l=fredrik-j.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://fredrik-j.blogspot.com/feeds/2807753352375203858/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=4754734402679928849&amp;postID=2807753352375203858' title='7 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4754734402679928849/posts/default/2807753352375203858'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4754734402679928849/posts/default/2807753352375203858'/><link rel='alternate' type='text/html' href='http://fredrik-j.blogspot.com/2009/07/another-mathematica-bug.html' title='Another Mathematica bug'/><author><name>Fredrik Johansson</name><uri>http://www.blogger.com/profile/01465400860530971858</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>7</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4754734402679928849.post-251927341570305284</id><published>2009-07-10T21:18:00.015+02:00</published><updated>2009-07-10T23:56:33.820+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='sage'/><category scheme='http://www.blogger.com/atom/ns#' term='sympy'/><category scheme='http://www.blogger.com/atom/ns#' term='mpmath'/><title type='text'>Improved incomplete gamma and exponential integrals; Clausen functions</title><content type='html'>The SVN trunk of mpmath now contains much improved implementations of the &lt;a href="http://en.wikipedia.org/wiki/Incomplete_gamma_function"&gt;incomplete gamma function&lt;/a&gt; (&lt;tt&gt;gammainc()&lt;/tt&gt;) as well as the &lt;a href="http://en.wikipedia.org/wiki/Exponential_integral"&gt;exponential integrals&lt;/a&gt; (&lt;tt&gt;ei()&lt;/tt&gt;, &lt;tt&gt;e1()&lt;/tt&gt;, &lt;tt&gt;expint()&lt;/tt&gt;). Although the code is not quite perfect yet, this was a rather tedious undertaking, so I'm probably going to work on something entirely different for a while and give these functions another iteration later.&lt;br /&gt;&lt;br /&gt;The incomplete gamma function comes in three flavors: the lower incomplete gamma function, the upper incomplete gamma function, and the generalized (two-endpoints) incomplete gamma function. The generalized incomplete gamma function is defined as&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://1.bp.blogspot.com/_rh0QblLk0C0/Slej9qCJKTI/AAAAAAAAAHY/jy2JNs_1H1M/s1600-h/gammainc.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 191px; height: 45px;" src="http://1.bp.blogspot.com/_rh0QblLk0C0/Slej9qCJKTI/AAAAAAAAAHY/jy2JNs_1H1M/s400/gammainc.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5356930561431120178" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;which reduces to the lower function when a = 0, and the upper version when b = +&amp;infin;. A huge number of integrals occurring in pure and applied mathematics have this form (even Gaussian integrals, with a change of variables) so a solid incomplete gamma is quite important. It's especially important to ensure both speed in correctness in asymptotic cases.&lt;br /&gt;&lt;br /&gt;The lower incomplete gamma function is easiest to implement, because it's essentially just a rescaled version of the hypergeometric 1F1 function and the 1F1 implementation already works well. Not much change was made here, so I'm going to write some about the other cases instead.&lt;br /&gt;&lt;br /&gt;The exponential integrals are essentially the same as the upper incomplete gamma function and mostly share code. Remarks about the upper gamma function therefore also apply to the exponential integrals.&lt;br /&gt;&lt;br /&gt;&lt;h4&gt;Upper gamma performance&lt;/h4&gt;&lt;br /&gt;&lt;br /&gt;The upper incomplete gamma function is hard because the two main series representations are an asymptotic series involving 2F0 that doesn't always converge, and an 1F1 series that suffers badly from cancellation for even moderately large arguments. The problem is to decide when to use which series.&lt;br /&gt;&lt;br /&gt;The 2F0 series is very fast when it converges, while the 1F1 series is quite slow (due to the need for extra precision) just below the point where 2F0 starts to converge. After some experimentation, I decided to change the implementation of 2F0. Instead of performing a heuristic, conservative test to determine whether the series will converge (sometimes claiming falsely that it won't), it now always goes ahead to sum the series and raises an error only when it actually doesn't converge.&lt;br /&gt;&lt;br /&gt;Thus the asymptotic series will always be used when possible, and although this leads to a slight slowdown for smaller arguments, it avoids worst-case slowness. The most important use for the incomplete gamma function, I believe, is in asymptotics, so I think this is a correct priority.&lt;br /&gt;&lt;br /&gt;As a result, you can now do this:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;&gt;&gt;&gt; from mpmath import *&lt;br /&gt;&gt;&gt;&gt; mp.dps = 25; mp.pretty = True&lt;br /&gt;&gt;&gt;&gt; gammainc(10, 100)&lt;br /&gt;4.083660630910611272288592e-26&lt;br /&gt;&gt;&gt;&gt; gammainc(10, 10000000000000000)&lt;br /&gt;5.290402449901174752972486e-4342944819032375&lt;br /&gt;&gt;&gt;&gt; gammainc(3+4j, 1000000+1000000j)&lt;br /&gt;(-1.257913707524362408877881e-434284 + 2.556691003883483531962095e-434284j)&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;The following graph compares old and new performance. The y axis shows the reciprocal time (higher is better) for computing gammainc(3.5, x) as x ranges between 0 and 100, at the standard precision of 15 digits. Red is the old implementation and blue is the new. The code also works with complex numbers, of course; replacing &lt;tt&gt;x&lt;/tt&gt; with &lt;tt&gt;j*x&lt;/tt&gt; gives a virtually identical graph (slightly scaled down due to the general overhead of complex arithmetic).&lt;br /&gt; &lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_rh0QblLk0C0/SleV2u5XumI/AAAAAAAAAHA/rjBs0Kd0--8/s1600-h/gammainc_35.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 400px; height: 302px;" src="http://3.bp.blogspot.com/_rh0QblLk0C0/SleV2u5XumI/AAAAAAAAAHA/rjBs0Kd0--8/s400/gammainc_35.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5356915049314630242" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;It's very visible where the asymptotic series kicks in, and the speed from then on is about 2000 evaluations per second which is relatively good. The new implementation is regrettably up to 3x slower than the old one for smaller x, although the slowdown is a bit misleading since the old version was broken and gave inaccurate results. The big dip in the blue graph at x = 10 is due to the automatic cancellation correction which the old code didn't use.&lt;br /&gt;&lt;br /&gt;The gap between the asymptotic and non-asymptotic cases could be closed by using specialized series code for the lower incomplete gamma function, or using the Legendre continued fraction for intermediate cases (this comes with some problems however, such as accurately estimating the rate of convergence, and the higher overhead for evaluating a continued fraction than a series). This will certainly be worth doing, but I'm not going to pursue those optimizations right now for reasons already stated.&lt;br /&gt;&lt;br /&gt;Some good news is that the graph above shows worst-case behavior, where the generic code is used, due the parameter 3.5. I've also implemented fast special-purpose code for the case when the first parameter is a (reasonably small) integer. This also means that the exponential integrals E1(x), Ei(x) as well as En(x) for integer n can be evaluated efficiently.&lt;br /&gt;&lt;br /&gt;Here is a speed comparison between the old and new implementations of the &lt;tt&gt;ei(x)&lt;/tt&gt; function, again at standard precision. There is actually no change in algorithm here: the old implementation used a Taylor series for small arguments and an asymptotic series for large arguments. The difference is due to using only low-level code; this turned out to buy a factor 2 in the Taylor case and more than an order of magnitude (!) in the asymptotic case.&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://2.bp.blogspot.com/_rh0QblLk0C0/SleaHekzibI/AAAAAAAAAHI/2xigFv8rRM4/s1600-h/ei_speed.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 400px; height: 302px;" src="http://2.bp.blogspot.com/_rh0QblLk0C0/SleaHekzibI/AAAAAAAAAHI/2xigFv8rRM4/s400/ei_speed.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5356919735037692338" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;The results are similar for the E1 function and with a complex argument. It is similar (only a bit slower) for &lt;tt&gt;gammainc(n,x)&lt;/tt&gt; and &lt;tt&gt;expint(n,x)&lt;/tt&gt; with a small integer value for n, although so far fast code is only implemented for real x in those cases.&lt;br /&gt;&lt;br /&gt;&lt;h4&gt;Accurate generalized incomplete gamma function&lt;/h4&gt;&lt;br /&gt;&lt;br /&gt;The generalized incomplete gamma function can be written either as the difference of two upper gammas, or as the difference of two lower gammas. Which representation is better depends on the arguments. In general, one will work while the other will lead to total cancellation. &lt;tt&gt;gammainc&lt;/tt&gt; is now clever enough to switch representations.&lt;br /&gt;&lt;br /&gt;This uses a difference of lower gamma functions behind the scenes:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;&gt;&gt;&gt; gammainc(10000000, 3) - gammainc(10000000, 2)   # Bad&lt;br /&gt;0.0&lt;br /&gt;&gt;&gt;&gt; gammainc(10000000, 2, 3)   # Good&lt;br /&gt;1.755146243738946045873491e+4771204&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;This uses a difference of upper gamma functions behind the scenes:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;&gt;&gt;&gt; gammainc(2, 0, 100000001) - gammainc(2, 0, 100000000)   # Bad&lt;br /&gt;0.0&lt;br /&gt;&gt;&gt;&gt; gammainc(2, 100000000, 100000001)   # Good&lt;br /&gt;4.078258353474186729184421e-43429441&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;h4&gt;Some demo plots&lt;/h4&gt;&lt;br /&gt;&lt;br /&gt;Here are two plots of the upper gamma functions and exponential integrals (for various values of the first parameter). A lot of time went into getting the correct branch cuts in the low-level code (and writing tests for them), so please appreciate the view of the imaginary parts.&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;T1 = lambda x: gammainc(-2,x)&lt;br /&gt;T2 = lambda x: gammainc(-1,x)&lt;br /&gt;T3 = lambda x: gammainc(0,x)&lt;br /&gt;T4 = lambda x: gammainc(1,x)&lt;br /&gt;T5 = lambda x: gammainc(2,x)&lt;br /&gt;plot([T1,T2,T3,T4,T5],[-5,5],[-10,10])&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_rh0QblLk0C0/Slen77NC-kI/AAAAAAAAAHg/-VtD-v5gmBo/s1600-h/plot_gammainc.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 400px; height: 302px;" src="http://3.bp.blogspot.com/_rh0QblLk0C0/Slen77NC-kI/AAAAAAAAAHg/-VtD-v5gmBo/s400/plot_gammainc.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5356934929727027778" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;T1 = lambda x: expint(-2,x)&lt;br /&gt;T2 = lambda x: expint(-1,x)&lt;br /&gt;T3 = lambda x: expint(0,x)&lt;br /&gt;T4 = lambda x: expint(1,x)&lt;br /&gt;T5 = lambda x: expint(2,x)&lt;br /&gt;plot([T1,T2,T3,T4,T5],[-5,5],[-10,10])&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://1.bp.blogspot.com/_rh0QblLk0C0/Slen_iuNpdI/AAAAAAAAAHo/-vtD2kUt5Bc/s1600-h/plot_expint.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 400px; height: 302px;" src="http://1.bp.blogspot.com/_rh0QblLk0C0/Slen_iuNpdI/AAAAAAAAAHo/-vtD2kUt5Bc/s400/plot_expint.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5356934991874729426" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;And a complex plot of &lt;tt&gt;gammainc(3+4j, 1/z)&lt;/tt&gt;:&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_rh0QblLk0C0/Slesn9C7cGI/AAAAAAAAAHw/8YqS1HtPjic/s1600-h/gic.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 400px; height: 313px;" src="http://4.bp.blogspot.com/_rh0QblLk0C0/Slesn9C7cGI/AAAAAAAAAHw/8YqS1HtPjic/s400/gic.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5356940084182216802" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;A plot of &lt;tt&gt;gammainc(1/z, -1/z, 1/z)&lt;/tt&gt;; a rather nonsensical function (but that is besides the point):&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_rh0QblLk0C0/SlezSj4varI/AAAAAAAAAII/jHZ7AmehieE/s1600-h/gammaweird.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 400px; height: 302px;" src="http://3.bp.blogspot.com/_rh0QblLk0C0/SlezSj4varI/AAAAAAAAAII/jHZ7AmehieE/s400/gammaweird.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5356947413232741042" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;h4&gt;Clausen functions&lt;/h4&gt;&lt;br /&gt;&lt;br /&gt;Unrelated to the gamma functions, I've also implemented &lt;a href="http://en.wikipedia.org/wiki/Clausen_function"&gt;Clausen functions&lt;/a&gt;:&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_rh0QblLk0C0/SlewEMxS_jI/AAAAAAAAAH4/jmINZeX43NE/s1600-h/clsin.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 163px; height: 51px;" src="http://4.bp.blogspot.com/_rh0QblLk0C0/SlewEMxS_jI/AAAAAAAAAH4/jmINZeX43NE/s400/clsin.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5356943867974450738" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_rh0QblLk0C0/SlewESBspCI/AAAAAAAAAIA/mJuJxuwYpcY/s1600-h/clcos.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 165px; height: 51px;" src="http://3.bp.blogspot.com/_rh0QblLk0C0/SlewESBspCI/AAAAAAAAAIA/mJuJxuwYpcY/s400/clcos.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5356943869385417762" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;These functions are just polylogarithms in disguise, but convenient as standalone functions. With them one can evaluate certain divergent Fourier series for example:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;&gt;&gt;&gt; clsin(-2, 3)&lt;br /&gt;-0.01781786725924798006896962&lt;br /&gt;&gt;&gt;&gt; nsum(lambda k: k**2 * sin(3*k), [1,inf])&lt;br /&gt;-0.01781786725924798006896962&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;They also work for complex arguments (and are related to zeta functions):&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;&gt;&gt;&gt; clsin(2+3j, 1+2j)&lt;br /&gt;(1.352229437254898401329125 + 1.401881614736751520048876j)&lt;br /&gt;&gt;&gt;&gt; clcos(2+3j, pi)&lt;br /&gt;(-1.042010539574581174661637 - 0.2070574989958949174656102j)&lt;br /&gt;&gt;&gt;&gt; altzeta(2+3j)&lt;br /&gt;(1.042010539574581174661637 + 0.2070574989958949174656102j)&lt;br /&gt;&gt;&gt;&gt; chop(clcos(zetazero(2), pi/2))&lt;br /&gt;0.0&lt;br /&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4754734402679928849-251927341570305284?l=fredrik-j.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://fredrik-j.blogspot.com/feeds/251927341570305284/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=4754734402679928849&amp;postID=251927341570305284' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4754734402679928849/posts/default/251927341570305284'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4754734402679928849/posts/default/251927341570305284'/><link rel='alternate' type='text/html' href='http://fredrik-j.blogspot.com/2009/07/improved-incomplete-gamma-and.html' title='Improved incomplete gamma and exponential integrals; Clausen functions'/><author><name>Fredrik Johansson</name><uri>http://www.blogger.com/profile/01465400860530971858</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://1.bp.blogspot.com/_rh0QblLk0C0/Slej9qCJKTI/AAAAAAAAAHY/jy2JNs_1H1M/s72-c/gammainc.png' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4754734402679928849.post-937208409179159156</id><published>2009-06-29T23:04:00.011+02:00</published><updated>2009-06-30T01:45:54.815+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='sage'/><category scheme='http://www.blogger.com/atom/ns#' term='sympy'/><category scheme='http://www.blogger.com/atom/ns#' term='mpmath'/><title type='text'>Meijer G, more hypergeometric functions, fractional differentiation</title><content type='html'>My &lt;a href="http://fredrik-j.blogspot.com/2009/06/massive-hypergeometric-update.html"&gt;last update&lt;/a&gt; and the post before it detailed substantial improvements to the code for hypergeometric functions in mpmath, specifically the support for asymptotic expansions for 0F1, 1F1, 2F1, 2F0, plus the ability to evaluate hypergeometric-type formulas with singular parameters.&lt;br /&gt;&lt;br /&gt;Over the past week-and-a-half I've done more work along the same lines. Importantly, I've implemented asymptotic expansions also for 1F2, 2F2 and 2F3 (commits &lt;a href="http://code.google.com/p/mpmath/source/detail?r=939"&gt;1&lt;/a&gt;, &lt;a href="http://code.google.com/p/mpmath/source/detail?r=941"&gt;2&lt;/a&gt;), so all hypergeometric functions of degree up to (2,3) now support fast evaluation for |z| → ∞ (1F0 also works, if anyone wonders -- it just happens to be a trivial case).&lt;br /&gt;&lt;br /&gt;The next major remaining case is 3F2. It has a 1/z transformation, but this leaves |z| ≈ 1 which I don't know how to deal with. Does anyone who happens to be reading this know methods for evaluating 3F2 close to the unit circle? Taylor expansion around some point other than 0 works to some extent, but it's slow and in particular asymptotically slow close to z = 1, so not much help.&lt;br /&gt;&lt;br /&gt;&lt;h4&gt;Bessel functions, etc.&lt;/h4&gt;&lt;br /&gt;As expected, the &lt;tt&gt;hypercomb&lt;/tt&gt; function leads to very simple implementations of a large class of functions. I've now implemented the Whittaker, Struve, and Kelvin functions (they are called &lt;tt&gt;whitm&lt;/tt&gt;, &lt;tt&gt;whitw&lt;/tt&gt;, &lt;tt&gt;struveh&lt;/tt&gt;, &lt;tt&gt;struvel&lt;/tt&gt;, &lt;tt&gt;ber&lt;/tt&gt;, &lt;tt&gt;bei&lt;/tt&gt;, &lt;tt&gt;ker&lt;/tt&gt;, &lt;tt&gt;kei&lt;/tt&gt;). I've yet to update the orthogonal polynomials, but that shouldn't be much work. With this, I will have covered most of &lt;a href="http://functions.wolfram.com/Bessel-TypeFunctions/"&gt;Bessel-type&lt;/a&gt; and &lt;a href="http://functions.wolfram.com/HypergeometricFunctions/"&gt;hypergeometric-type&lt;/a&gt; functions listed on the Wolfram Functions site.&lt;br /&gt;&lt;br /&gt;Speaking of Bessel functions, I also addressed most of the problems with their implementation in &lt;a href="http://code.google.com/p/mpmath/source/detail?r=945"&gt;this commit&lt;/a&gt;. In particular, they can now be evaluated for huge arguments:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;&gt;&gt;&gt; mp.dps = 30&lt;br /&gt;&gt;&gt;&gt; print besselj(1, 10**20)&lt;br /&gt;-7.95068198242545016504555020084e-11&lt;br /&gt;&gt;&gt;&gt; print chop(besselj(1, 10**20 * j))&lt;br /&gt;(0.0 + 5.17370851996688078482437428203e+43429448190325182754j)&lt;br /&gt;&gt;&gt;&gt; print bessely(1,10**20)&lt;br /&gt;-6.69800904070342428527377044712e-12&lt;br /&gt;&gt;&gt;&gt; print besselk(1,10**20)&lt;br /&gt;9.66424757155048856421325779143e-43429448190325182776&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;This wasn't trivial, mainly because although &lt;tt&gt;hypercomb&lt;/tt&gt; generally works, it sometimes becomes impossibly slow when computing functions straight from the definition. Basically: the function of interest might decrease exponentially, but internally it is computed by adding two nearly identical terms that grow exponentially, so the working precision and computation time increases exponentially. It's therefore still necessary to switch between different representations in different parts of the complex plane, and figuring that out involves some work. Some cases probably remain to be fixed.&lt;br /&gt;&lt;br /&gt;As a followup to last week, I'll attach plots of &lt;i&gt;J&lt;/i&gt;&lt;sub&gt;0&lt;/sub&gt;(1/z&lt;sup&gt;3&lt;/sup&gt;), &lt;i&gt;Y&lt;/i&gt;&lt;sub&gt;0&lt;/sub&gt;(1/z&lt;sup&gt;3&lt;/sup&gt;) and &lt;i&gt;K&lt;/i&gt;&lt;sub&gt;0&lt;/sub&gt;(1/z&lt;sup&gt;3&lt;/sup&gt;):&lt;table&gt;&lt;br /&gt;&lt;tr&gt;&lt;br /&gt;&lt;td&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_rh0QblLk0C0/SklFEHSHxLI/AAAAAAAAAGk/wM-wo1wXrEU/s1600-h/besj.png"&gt;&lt;img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer; width: 200px; height: 192px;" src="http://3.bp.blogspot.com/_rh0QblLk0C0/SklFEHSHxLI/AAAAAAAAAGk/wM-wo1wXrEU/s200/besj.png" alt="" id="BLOGGER_PHOTO_ID_5352885569083393202" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;/td&gt;&lt;br /&gt;&lt;td&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_rh0QblLk0C0/SklFKMeTpqI/AAAAAAAAAGs/zJYLZ-dyB10/s1600-h/besy.png"&gt;&lt;img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer; width: 200px; height: 192px;" src="http://3.bp.blogspot.com/_rh0QblLk0C0/SklFKMeTpqI/AAAAAAAAAGs/zJYLZ-dyB10/s200/besy.png" alt="" id="BLOGGER_PHOTO_ID_5352885673555895970" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;/td&gt;&lt;br /&gt;&lt;td&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://2.bp.blogspot.com/_rh0QblLk0C0/SklNSPKKCXI/AAAAAAAAAG0/Abf3dRaJGt8/s1600-h/besk.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 200px; height: 193px;" src="http://2.bp.blogspot.com/_rh0QblLk0C0/SklNSPKKCXI/AAAAAAAAAG0/Abf3dRaJGt8/s200/besk.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5352894607808661874" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;/td&gt;&lt;br /&gt;&lt;/tr&gt;&lt;br /&gt;&lt;/table&gt;&lt;br /&gt;&lt;br /&gt;The &lt;i&gt;K&lt;/i&gt; plot unfortunately took very long time to finish -- almost an hour for 200,000 complex evaluations (&lt;i&gt;J&lt;/i&gt; and &lt;i&gt;Y&lt;/i&gt; were both much faster), so I'll probably have to optimize &lt;tt&gt;besselk&lt;/tt&gt; a bit further.&lt;br /&gt;&lt;br /&gt;&lt;h4&gt;Fractional derivatives&lt;/h4&gt;&lt;br /&gt;&lt;br /&gt;Another useful enhancement to the Bessel functions is that they can now be differentiated and integrated directly:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;&gt;&gt;&gt; mp.dps = 30&lt;br /&gt;&gt;&gt;&gt; print besselj(0, 3.5, derivative=2)&lt;br /&gt;0.419378462090785430440501275011&lt;br /&gt;&gt;&gt;&gt; print diff(lambda x: besselj(0,x), 3.5, 2)&lt;br /&gt;0.419378462090785430440501275011&lt;br /&gt;&gt;&gt;&gt; print besselj(0,3.5,derivative=-1) - besselj(0,2.5,derivative=-1)&lt;br /&gt;-0.244675206320579138611991019242&lt;br /&gt;&gt;&gt;&gt; print quad(lambda x: besselj(0,x), [2.5, 3.5])&lt;br /&gt;-0.244675206320579138611991019242&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;In fact, the representation for the derivatives works not just for integer orders (including negative values -- giving iterated integrals), but also for fractional or complex values. This led me to implement a general function &lt;tt&gt;differint&lt;/tt&gt; for computing fractional order derivatives / integrals of arbitrary functions. (&lt;a href="http://code.google.com/p/mpmath/source/detail?r=946"&gt;Commit&lt;/a&gt;.) It works essentially directly with the definition of the &lt;a href="http://en.wikipedia.org/wiki/Riemann-Liouville_differintegral"&gt;Riemann-Liouville differintegral&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;It gives the same result as the special-purpose implementation for the Bessel function:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;&gt;&gt;&gt; print besselj(0, 3.5, derivative=0.5)&lt;br /&gt;-0.436609427860836504473775239357&lt;br /&gt;&gt;&gt;&gt; print differint(lambda x: besselj(0,x), 3.5, 0.5)&lt;br /&gt;-0.436609427860836504473775239357&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;One neat application is iterated integration. The following gives a 5-fold integral of f(x) = exp(&amp;pi; x), along with the symbolic evaluation of the same as a check:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;&gt;&gt;&gt; print differint(lambda x: exp(pi*x), 3.5, -5, x0=-inf)&lt;br /&gt;194.790546022218468869246881408&lt;br /&gt;&gt;&gt;&gt; print exp(pi*3.5) / pi**5&lt;br /&gt;194.790546022218468869246881408&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Does anyone have any other interesting applications for fractional differentiation? I'd be interested in more examples to test with and possibly add to the documentation.&lt;br /&gt;&lt;br /&gt;&lt;h4&gt;The Meijer G-function&lt;/h4&gt;&lt;br /&gt;At last, the &lt;a href="http://en.wikipedia.org/wiki/Meijer_G-function"&gt;Meijer G-function&lt;/a&gt; is implemented! (&lt;a href="http://code.google.com/p/mpmath/source/detail?r=948"&gt;Commit&lt;/a&gt;.) Personally, I think this is something of a milestone for the mpmath project.&lt;br /&gt;&lt;br /&gt;The Meijer G-function is very important because of its role in symbolic definite integration. Basically, definite integrals of Meijer G-functions (and even products of Meijer G-functions) just yield new Meijer G-functions; Mathematica and Maple therefore do many integrals by rewriting the input in terms of Meijer G-functions, applying Meijer G transformations, and converting the result back to simpler functions if possible.&lt;br /&gt;&lt;br /&gt;Having the Meijer G-function in mpmath should be useful for anyone who wishes to implement a more powerful definite integrator in SymPy for example. It could also be useful for obtaining numerical values from integrals done by hand.&lt;br /&gt;&lt;br /&gt;Looking around for examples to do stress testing with, I found a web page by Viktor Toth: &lt;a href="http://www.vttoth.com/MapleMeijerG.htm"&gt;Maple and Meijer's G-function: a numerical instability and a cure&lt;/a&gt;. His problem is to accurately evaluate G(-; 0; -1/2,-1,-3/2; -; x) for large real values of x. With my Meijer G-function implementation, I get:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;&gt;&gt;&gt; mp.dps = 15&lt;br /&gt;&gt;&gt;&gt; print meijerg([[],[0]],[[-0.5,-1,-1.5],[]],10)&lt;br /&gt;4.89717497704114e-5&lt;br /&gt;&gt;&gt;&gt; print meijerg([[],[0]],[[-0.5,-1,-1.5],[]],100)&lt;br /&gt;1.09696661341118e-12&lt;br /&gt;&gt;&gt;&gt; print meijerg([[],[0]],[[-0.5,-1,-1.5],[]],1000)&lt;br /&gt;0.0&lt;br /&gt;&gt;&gt;&gt; print meijerg([[],[0]],[[-0.5,-1,-1.5],[]],10000)&lt;br /&gt;1.53249554086589e+54&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;The third value should probably be small but not quite zero, and the last value is clearly bogus. Without looking at the details, the cause is almost certainly catastrophic cancellation of two huge terms. Fortunately, there is a cure for this:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;&gt;&gt;&gt; print meijerg([[],[0]],[[-0.5,-1,-1.5],[]],1000, check_cancellation=True)&lt;br /&gt;3.34093555343418e-33&lt;br /&gt;&gt;&gt;&gt; print meijerg([[],[0]],[[-0.5,-1,-1.5],[]],10000, check_cancellation=True)&lt;br /&gt;2.43925769071996e-94&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;The cancellation check should probably be enabled by default, either to automatically redo the computation as above or at least to issue a warning. The only catch with this is that it might lead to unnecessary slowdown and/or annoyance for the user in some cases, so I'll have to investigate how common those cases are.&lt;br /&gt;&lt;br /&gt;Incidentally, I also tried plugging the above two calculations into Mathematica 6.0. It takes a long time to finish (mpmath gives the result instantaneously) -- and returns values that are wrong!&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;In[1]:= u1 = MeijerG[{{},{0}},{{-1/2,-1,-3/2},{}},1000]&lt;br /&gt;&lt;br /&gt;                           3         1&lt;br /&gt;Out[1]= MeijerG[{{}, {0}}, {{-(-), -1, -(-)}, {}}, 1000]&lt;br /&gt;                           2         2&lt;br /&gt;&lt;br /&gt;In[2]:= u2 = MeijerG[{{},{0}},{{-1/2,-1,-3/2},{}},10000]&lt;br /&gt;&lt;br /&gt;                           3         1&lt;br /&gt;Out[2]= MeijerG[{{}, {0}}, {{-(-), -1, -(-)}, {}}, 10000]&lt;br /&gt;                           2         2&lt;br /&gt;&lt;br /&gt;In[3]:= Timing[N[u1,20]]&lt;br /&gt;&lt;br /&gt;Out[3]= {8.90265, 0.0017597930166135139087}&lt;br /&gt;&lt;br /&gt;In[4]:= Timing[N[u1,50]]&lt;br /&gt;&lt;br /&gt;                                                                    -33&lt;br /&gt;Out[4]= {12.8231, 3.3409355534341801158987353523397047765918571151576 10   }&lt;br /&gt;&lt;br /&gt;In[5]:=  Timing[N[u2,20]]&lt;br /&gt;&lt;br /&gt;                                      50&lt;br /&gt;Out[5]= {59.017, -2.0782671663885270791 10  }&lt;br /&gt;&lt;br /&gt;In[6]:= Timing[N[u2,50]]&lt;br /&gt;&lt;br /&gt;                                                                     22&lt;br /&gt;Out[6]= {83.3753, -2.8700325450226332558088281915945389986057044454640 10  }&lt;br /&gt;&lt;br /&gt;In[7]:= Timing[N[u2,120]]&lt;br /&gt;&lt;br /&gt;Out[7]= {451.365, 2.439257690719956395903324691434088756714300374716395499173\&lt;br /&gt;&lt;br /&gt;                                                                    -94&lt;br /&gt;&gt;      70196218529840153673260714339051464703903148052541923961351654 10   }&lt;br /&gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;That's several minutes for something mpmath did in less than a second, and with less intervention. So maybe Maple and Mathematica should copy my Meijer G-function code ;-)&lt;br /&gt;&lt;br /&gt;&lt;h4&gt;Complex roots&lt;/h4&gt;&lt;br /&gt;&lt;br /&gt;A small and trivial, but quite convenient, new feature: the &lt;tt&gt;nthroot&lt;/tt&gt; function can now compute any of the roots of a given number and not just the principal root. I also added &lt;tt&gt;root&lt;/tt&gt; as an alias since I have lazy fingers. Like so:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;&gt;&gt;&gt; for k in range(5):&lt;br /&gt;...     r = root(-12, 5, k)&lt;br /&gt;...     print chop(r**5), r&lt;br /&gt;...&lt;br /&gt;-12.0 (1.32982316461435 + 0.966173083818997j)&lt;br /&gt;-12.0 (-0.507947249855734 + 1.56330088863444j)&lt;br /&gt;-12.0 -1.64375182951723&lt;br /&gt;-12.0 (-0.507947249855734 - 1.56330088863444j)&lt;br /&gt;-12.0 (1.32982316461435 - 0.966173083818997j)&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;While I was at it, I couldn't resist also implementing a function &lt;tt&gt;unitroots&lt;/tt&gt; for computing all the nth roots of unity, optionally just the primitive roots, as well as a function &lt;tt&gt;cyclotomic&lt;/tt&gt; for evaluating the nth cyclotomic polynomial. As it turns out, cyclotomic polynomials are not entirely trivial to evaluate both efficiently and in a numerically stable way -- see the source code for my solution. &lt;a href="http://code.google.com/p/mpmath/source/detail?r=947"&gt;Commit here&lt;/a&gt;. A side effect is that I also implemented a &lt;tt&gt;powm1&lt;/tt&gt; function that accurately gives x&lt;sup&gt;y&lt;/sup&gt; - 1 (possibly a useful complement to &lt;tt&gt;expm1&lt;/tt&gt; for other uses as well):&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;&gt;&gt;&gt; print power(2,1e-100)-1&lt;br /&gt;0.0&lt;br /&gt;&gt;&gt;&gt; print powm1(2, 1e-100)&lt;br /&gt;6.93147180559945e-101&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;That will be all for now.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4754734402679928849-937208409179159156?l=fredrik-j.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://fredrik-j.blogspot.com/feeds/937208409179159156/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=4754734402679928849&amp;postID=937208409179159156' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4754734402679928849/posts/default/937208409179159156'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4754734402679928849/posts/default/937208409179159156'/><link rel='alternate' type='text/html' href='http://fredrik-j.blogspot.com/2009/06/meijer-g-more-hypergeometric-functions.html' title='Meijer G, more hypergeometric functions, fractional differentiation'/><author><name>Fredrik Johansson</name><uri>http://www.blogger.com/profile/01465400860530971858</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://3.bp.blogspot.com/_rh0QblLk0C0/SklFEHSHxLI/AAAAAAAAAGk/wM-wo1wXrEU/s72-c/besj.png' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4754734402679928849.post-180874788199283824</id><published>2009-06-19T14:55:00.014+02:00</published><updated>2009-06-19T18:37:11.444+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='sage'/><category scheme='http://www.blogger.com/atom/ns#' term='sympy'/><category scheme='http://www.blogger.com/atom/ns#' term='mpmath'/><title type='text'>Massive hypergeometric update</title><content type='html'>[&lt;i&gt;Update&lt;/i&gt;: as further proof that the asymptotic expansions are working, I've plotted 1/erfi(1/z&lt;sup&gt;3&lt;/sup&gt;), Ai(1/z&lt;sup&gt;3&lt;/sup&gt;) and Bi(1/z&lt;sup&gt;3&lt;/sup&gt;) around 0: &lt;table&gt;&lt;br /&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://1.bp.blogspot.com/_rh0QblLk0C0/Sju-j87A5NI/AAAAAAAAAGM/VUMvMsgqBSI/s1600-h/erfi_inverted_3.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 200px; height: 192px;" src="http://1.bp.blogspot.com/_rh0QblLk0C0/Sju-j87A5NI/AAAAAAAAAGM/VUMvMsgqBSI/s200/erfi_inverted_3.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5349078507290944722" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;/td&gt;&lt;br /&gt;&lt;td&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_rh0QblLk0C0/Sju-kM8RLMI/AAAAAAAAAGU/iayEX-Ru9BI/s1600-h/airyai_inverted_3.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 200px; height: 192px;" src="http://3.bp.blogspot.com/_rh0QblLk0C0/Sju-kM8RLMI/AAAAAAAAAGU/iayEX-Ru9BI/s200/airyai_inverted_3.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5349078511591173314" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;/td&gt;&lt;br /&gt;&lt;td&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://2.bp.blogspot.com/_rh0QblLk0C0/Sju-kSUbQmI/AAAAAAAAAGc/ayqOHBpaVDo/s1600-h/airybi_inverted_3.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 200px; height: 192px;" src="http://2.bp.blogspot.com/_rh0QblLk0C0/Sju-kSUbQmI/AAAAAAAAAGc/ayqOHBpaVDo/s200/airybi_inverted_3.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5349078513034674786" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt; &lt;i&gt;End of update.&lt;/i&gt;]&lt;br /&gt;&lt;br /&gt;Today I committed a &lt;a href="http://code.google.com/p/mpmath/source/detail?r=936"&gt;large patch&lt;/a&gt; to mpmath that significantly improves the state of hypergeometric functions. It's the result of about a week of work (plus some earlier research).&lt;br /&gt;&lt;br /&gt;Perhaps most importantly, I've implemented the asymptotic expansions for 0F1 and 1F1 (the expansions for 2F1 were discussed in the &lt;a href="http://fredrik-j.blogspot.com/2009/06/hypergeometric-2f1-incomplete-beta.html"&gt;previous post&lt;/a&gt;). They should now work for arbitrarily large arguments, as such:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;&gt;&gt;&gt; from mpmath import *&lt;br /&gt;&gt;&gt;&gt; mp.dps = 25&lt;br /&gt;&gt;&gt;&gt; print hyp0f1(3,100)&lt;br /&gt;786255.7044208151250793134&lt;br /&gt;&gt;&gt;&gt; print hyp0f1(3,100000000)&lt;br /&gt;4.375446848722142947128962e+8675&lt;br /&gt;&gt;&gt;&gt; print hyp0f1(3,10**50)&lt;br /&gt;4.263410645749930620781402e+8685889638065036553022515&lt;br /&gt;&gt;&gt;&gt; print hyp0f1(3,-10**50)&lt;br /&gt;-2.231890729584050840600415e-63&lt;br /&gt;&gt;&gt;&gt; print hyp0f1(1000,1000+10**8*j)&lt;br /&gt;(-1.101783528465991973738237e+4700 - 1.520418042892352360143472e+4700j)&lt;br /&gt;&gt;&gt;&gt; print hyp1f1(2,3,10**10)&lt;br /&gt;2.15550121570157969883678e+4342944809&lt;br /&gt;&gt;&gt;&gt; print hyp1f1(2,3,-10**10)&lt;br /&gt;2.0e-20&lt;br /&gt;&gt;&gt;&gt; print hyp1f1(2,3,10**10*j)&lt;br /&gt;(-9.750120502003974585202174e-11 - 1.746239245451213207369885e-10j)&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;I also implemented 2F0 and U (Kummer's second function), mostly as a byproduct of the fact that 2F0 is needed for the asymptotic expansions of both 0F1 and 1F1. 2F0 is an interesting function: it is given by a divergent series (it converges only in special cases where it terminates after finitely many steps):&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_rh0QblLk0C0/SjuZe79L5vI/AAAAAAAAAFc/7XStPwRuN6I/s1600-h/45cce43e5a69cbc0f122c3e30cd69e17e32688de.png"&gt;&lt;img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer; width: 228px; height: 51px;" src="http://4.bp.blogspot.com/_rh0QblLk0C0/SjuZe79L5vI/AAAAAAAAAFc/7XStPwRuN6I/s400/45cce43e5a69cbc0f122c3e30cd69e17e32688de.png" alt="" id="BLOGGER_PHOTO_ID_5349037739202045682" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;However, it can be assigned a finite value for all &lt;i&gt;z&lt;/i&gt; by expressing it in terms of U. Hence, mpmath can now compute the regularized sum of 2F0(&lt;i&gt;a&lt;/i&gt;,&lt;i&gt;b&lt;/i&gt;,&lt;i&gt;z&lt;/i&gt;) for any arguments, say these ones:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;&gt;&gt;&gt; print hyp2f0(5, -1.5, 4)&lt;br /&gt;(0.0000005877300438912428637649737 + 89.51091139854661783977495j)&lt;br /&gt;&gt;&gt;&gt; print hyp2f0(5, -1.5, -4)&lt;br /&gt;102.594435262256516621777&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;(This ought to be a novel feature; SciPy and GSL implement 2F0, but only special cases thereof, and Mathematica doesn't have a direct way to evaluate 2F0.)&lt;br /&gt;&lt;br /&gt;It's the asymptotic case where &lt;i&gt;z&lt;/i&gt; → 0, where a truncation of the 2F0 series can be used, that is used for the expansions at infinity of 0F1 and 1F1.&lt;br /&gt;&lt;br /&gt;Back to 0F1 and 1F1, the asymptotic expansions of these functions are important because they permit many special functions to be evaluated efficiently for large arguments. So far I've fixed &lt;tt&gt;erf&lt;/tt&gt;, &lt;tt&gt;erfc&lt;/tt&gt;, &lt;tt&gt;erfi&lt;/tt&gt;, &lt;tt&gt;airyai&lt;/tt&gt; and &lt;tt&gt;airybi&lt;/tt&gt; to take advantage of this fact (except for erf and erfc of a real variable, all these functions were previously slow even for a moderately large argument, say |z| &gt; 100).&lt;br /&gt;&lt;br /&gt;Examples that now work (well, some of them possibly theoretically worked before too, but probably required hours or ages of universe to finish):&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;&gt;&gt;&gt; print erf(10000+10000j)&lt;br /&gt;(1.000001659143196966967784 - 0.00003985971242709750831972313j)&lt;br /&gt;&gt;&gt;&gt; print erfi(1000000000)&lt;br /&gt;2.526701758277367028229496e+434294481903251818&lt;br /&gt;&gt;&gt;&gt; print erfc(1000-5j)&lt;br /&gt;(-1.27316023652348267063187e-434287 - 4.156805871732993710222905e-434288j)&lt;br /&gt;&gt;&gt;&gt; print airyai(10**10)&lt;br /&gt;1.162235978298741779953693e-289529654602171&lt;br /&gt;&gt;&gt;&gt; print airybi(10**10)&lt;br /&gt;1.369385787943539818688433e+289529654602165&lt;br /&gt;&gt;&gt;&gt; print airyai(-10**10)&lt;br /&gt;0.0001736206448152818510510181&lt;br /&gt;&gt;&gt;&gt; print airybi(-10**10)&lt;br /&gt;0.001775656141692932747610973&lt;br /&gt;&gt;&gt;&gt; print airyai(10**10 * (1+j))&lt;br /&gt;(5.711508683721355528322567e-186339621747698 + 1.867245506962312577848166e-186339621747697j)&lt;br /&gt;&gt;&gt;&gt; print airybi(10**10 * (1+j))&lt;br /&gt;(-6.559955931096196875845858e+186339621747689 - 6.822462726981357180929024e+186339621747690j)&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;An essential addition in this patch is the function &lt;tt&gt;hypercomb&lt;/tt&gt; which evaluates a linear combination of hypergeometric series, with gamma function and power weights:&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://1.bp.blogspot.com/_rh0QblLk0C0/SjuNqhChzKI/AAAAAAAAAFM/FRZFId4OUkw/s1600-h/e27fcb391c1d049d96fbb687c549afd0f32eaf5e.png"&gt;&lt;img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer; width: 400px; height: 44px;" src="http://1.bp.blogspot.com/_rh0QblLk0C0/SjuNqhChzKI/AAAAAAAAAFM/FRZFId4OUkw/s400/e27fcb391c1d049d96fbb687c549afd0f32eaf5e.png" alt="" id="BLOGGER_PHOTO_ID_5349024743995591842" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;This is an extremely general function. Here is a partial list of functions that can be represented more or less &lt;i&gt;directly&lt;/i&gt; by means of it:&lt;br /&gt;&lt;br /&gt;&lt;ul&gt;&lt;br /&gt;&lt;li&gt;Regularized hypergeometric series&lt;/li&gt;&lt;br /&gt;&lt;li&gt;The generalized incomplete gamma and beta functions (and their regularizations)&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Bessel functions&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Airy, Whittaker, Kelvin, Struve functions, etc&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Error functions&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Exponential, trigonometric and hyperbolic integrals&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Legendre, Chebyshev, Jacobi, Laguerre, Gegenbauer polynomials&lt;/li&gt;&lt;br /&gt;&lt;li&gt;The Meijer G-function&lt;/li&gt;&lt;br /&gt;&lt;/ul&gt;&lt;br /&gt;That's most of &lt;a href="http://www.math.ucla.edu/%7Ecbm/aands/"&gt;Abramowitz &amp;amp; Stegun&lt;/a&gt;, and means that the remaining hypergeometric-type functions available in Mathematica or Maxima but absent in mpmath will be easy to implement in the near future. With these additions, mpmath will have the most comprehensive support for numerical hypergeometric-type functions of any open source software, and should be very close to Mathematica.&lt;br /&gt;&lt;br /&gt;The most important virtue of &lt;tt&gt;hypercomb&lt;/tt&gt; is not that it allows for more concise implementations of various hypergeometric-type functions, although that is a big advantage too. The main idea is that &lt;tt&gt;hypercomb&lt;/tt&gt; can deal with singular subexpressions, and particularly with gamma function poles that cancel against singularities in the hypergeometric series. These cases are almost more common than the nonsingular cases in practice, and &lt;tt&gt;hypercomb&lt;/tt&gt; saves the trouble of handling them in every separate function.&lt;br /&gt;&lt;br /&gt;Thus, in principle, a numerically correct implementation of &lt;tt&gt;hypercomb&lt;/tt&gt; leads to correct implementations of all the functions in the list above. It's not a silver bullet, of course. For example, if a particular but very common case of some common function triggers an expensive limit evaluation in &lt;tt&gt;hypercomb&lt;/tt&gt;, then it's probably better to handle that case with special-purpose code. There are also most likely some bugs left in &lt;tt&gt;hypercomb&lt;/tt&gt;, although by now I have tested a rather large set of examples; large enough to be confident that it works soundly.&lt;br /&gt;&lt;br /&gt;To show an example of how it works, an implementation of the Bessel J function might look like this:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;def besj(n,z):&lt;br /&gt; z = mpmathify(z)&lt;br /&gt; h = lambda n: [([z/2],[n],[],[n+1],[],[n+1],-(z/2)**2)]&lt;br /&gt; return hypercomb(h, [n])&lt;br /&gt;&lt;br /&gt;&gt;&gt;&gt; mp.dps = 30&lt;br /&gt;&gt;&gt;&gt; print besselj(3,10.5)&lt;br /&gt;0.1632801643733625756462387&lt;br /&gt;&gt;&gt;&gt; print besselj(-3,10.5)&lt;br /&gt;-0.1632801643733625756462387&lt;br /&gt;&gt;&gt;&gt; print besj(3,10.5)&lt;br /&gt;0.1632801643733625756462387&lt;br /&gt;&gt;&gt;&gt; print besj(-3,10.5)&lt;br /&gt;-0.1632801643733625756462387&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;It gives the same value as the current Bessel J implementation in mpmath. Note that it works even when &lt;i&gt;n&lt;/i&gt; is a negative integer, whereas naively evaluating the equation defining J(n,z) hits a gamma function pole and a division by zero in the hypergeometric series.&lt;br /&gt;&lt;br /&gt;Thanks to the support for asymptotic expansions, this implementation (unlike the current &lt;tt&gt;besselj&lt;/tt&gt; will also work happily with large arguments):&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;&gt;&gt;&gt; print besj(3,1e9)&lt;br /&gt;0.00000521042254280399021290721662036&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;I'm soon going to fix all the Bessel functions in mpmath along these lines, as I already did with &lt;tt&gt;erf&lt;/tt&gt;, &lt;tt&gt;airyai&lt;/tt&gt;, etc.&lt;br /&gt;&lt;br /&gt;Here is another, more involved example (quoting directly from the docstring for &lt;tt&gt;hypercomb&lt;/tt&gt;). The following evaluates&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://1.bp.blogspot.com/_rh0QblLk0C0/SjuQB3hIN2I/AAAAAAAAAFU/zeCXW28NKx8/s1600-h/9b711f253a2abc1bf33fb508c022517a889e9499.png"&gt;&lt;img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer; width: 400px; height: 42px;" src="http://1.bp.blogspot.com/_rh0QblLk0C0/SjuQB3hIN2I/AAAAAAAAAFU/zeCXW28NKx8/s400/9b711f253a2abc1bf33fb508c022517a889e9499.png" alt="" id="BLOGGER_PHOTO_ID_5349027344189765474" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;with &lt;i&gt;a&lt;/i&gt;=1, &lt;i&gt;z&lt;/i&gt;=3. There is a zero factor, two gamma function poles, and the 1F1 function is singular; all singularities cancel out to give a finite value:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;&gt;&gt;&gt; from mpmath import *&lt;br /&gt;&gt;&gt;&gt; mp.dps = 15&lt;br /&gt;&gt;&gt;&gt; print hypercomb(lambda a: [([a-1],[1],[a-3],[a-4],[a],[a-1],3)], [1])&lt;br /&gt;-180.769832308689&lt;br /&gt;&gt;&gt;&gt; print -9*exp(3)&lt;br /&gt;-180.769832308689&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;With some tweaks, the same code perhaps with a few tweaks could be used to symbolically evaluate hypergeometric-type functions (in the sense of rewriting them as pure hypergeometric series, and then possibly evaluating those symbolically as a second step). Symbolic support for hypergeometric functions is a very interesting (and hard) problem, and extremely important for computer algebra, but unfortunately I don't have time to work on that at the moment (there is more than enough to do just on the numerical side).&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4754734402679928849-180874788199283824?l=fredrik-j.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://fredrik-j.blogspot.com/feeds/180874788199283824/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=4754734402679928849&amp;postID=180874788199283824' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4754734402679928849/posts/default/180874788199283824'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4754734402679928849/posts/default/180874788199283824'/><link rel='alternate' type='text/html' href='http://fredrik-j.blogspot.com/2009/06/massive-hypergeometric-update.html' title='Massive hypergeometric update'/><author><name>Fredrik Johansson</name><uri>http://www.blogger.com/profile/01465400860530971858</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://1.bp.blogspot.com/_rh0QblLk0C0/Sju-j87A5NI/AAAAAAAAAGM/VUMvMsgqBSI/s72-c/erfi_inverted_3.png' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4754734402679928849.post-2181422470258036293</id><published>2009-06-11T18:39:00.008+02:00</published><updated>2009-06-11T21:34:14.280+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='sage'/><category scheme='http://www.blogger.com/atom/ns#' term='sympy'/><category scheme='http://www.blogger.com/atom/ns#' term='mpmath'/><title type='text'>Hypergeometric 2F1, incomplete beta, exponential integrals</title><content type='html'>One of the classes of functions I'm currently looking to improve in mpmath is the &lt;a href="http://en.wikipedia.org/wiki/Hypergeometric_series"&gt;hypergeometric functions&lt;/a&gt;; particularly 1F1 (equivalently the incomplete gamma function) and the Gauss hypergeometric function 2F1.&lt;br /&gt;&lt;br /&gt;For example, the classical orthogonal polynomials (Legendre, Chebyshev, Jacobi) are instances of 2F1 with certain integer parameters, and 2F1 with noninteger parameters allows for generalization of these functions to noninteger orders. Other functions that can be reduced to 2F1 include elliptic integrals (though mpmath uses AGM for these). With a good implementation of 2F1, these functions can be implemented very straightforwardly without a lot of special-purpose code to handle all their corner cases.&lt;br /&gt;&lt;br /&gt;Numerical evaluation of 2F1 is far from straightforward, and the &lt;tt&gt;hyp2f1&lt;/tt&gt; function in mpmath used to be quite fragile. The hypergeometric series only converges for |z| &lt; 1, and rapidly only for |z| &lt;&lt; 1. There is a transformation that replaces z with 1/z, but this leaves arguments close to the unit circle which must be handled using further transformations. As if things weren't complicated enough, the transformations involve gamma function factors that often become singular even when the value of 2F1 is actually finite, and obtaining the correct finite value involves appropriately cancelling the singularities against each other.&lt;br /&gt;&lt;br /&gt;After about two days of work, I've patched the 2F1 function in mpmath to the point where it should finally work for all complex values of a, b, c, z (see &lt;a href="http://code.google.com/p/mpmath/source/list"&gt;commits here&lt;/a&gt;). I'm not going to bet money that there isn't some problematic case left unhandled, but I've done tests for many of the special cases now.&lt;br /&gt;&lt;br /&gt;The following is a very simple example that previously triggered a division by zero but now works:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;&gt;&gt;&gt; print hyp2f1(3,-1,-1,0.5)&lt;br /&gt;2.5&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;The following previously returned something like -inf + nan*j, due to incorrect handling of gamma function poles, but now works:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;&gt;&gt;&gt; print hyp2f1(1,1,4,3+4j)&lt;br /&gt;(0.492343840009635 + 0.60513406166124j)&lt;br /&gt;&gt;&gt;&gt; print (717./1250-378j/625)-(6324./15625-4032j/15625)*log(-2-4j)  # Exact&lt;br /&gt;(0.492343840009635 + 0.60513406166124j)&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Evaluation close to the unit circle used to be completely broken, but should be fine now. A simple test is to integrate along the unit circle:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;&gt;&gt;&gt; mp.dps = 25&lt;br /&gt;&gt;&gt;&gt; a, b, c = 1.5, 2, -4.25&lt;br /&gt;&gt;&gt;&gt; print quad(lambda z: hyp2f1(a,b,c,exp(j*z)), [pi/2, 3*pi/2])&lt;br /&gt;(14.97223917917104676241015 + 1.70735170126956043188265e-24j)&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Mathematica gives the same value:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;In[17]:= NIntegrate[Hypergeometric2F1[3/2,2,-17/4,Exp[I z]],&lt;br /&gt;    {z, Pi/2, 3Pi/2}, WorkingPrecision-&gt;25]&lt;br /&gt;                                                                   -26&lt;br /&gt;Out[17]= 14.97223917917104676241014 - 3.514976640925973851950882 10    I&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Finally, evaluation at the singular point z = 1 now works and knows whether the result is finite or infinite:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;&gt;&gt;&gt; print hyp2f1(1, 0.5, 3, 1)&lt;br /&gt;1.333333333333333333333333&lt;br /&gt;&gt;&gt;&gt; print hyp2f1(1, 4.5, 3, 1)&lt;br /&gt;+inf&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;As a consequence of these improvements, several mpmath functions (such as the orthogonal polynomials) should now work for almost all complex parameters as well.&lt;br /&gt;&lt;br /&gt;The improvements to 2F1 also pave the way for some new functions. One of the many functions that can be reduced to 2F1 is the generalized incomplete beta function:&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_rh0QblLk0C0/SjFcIr_FWZI/AAAAAAAAAFE/7iGK83rQpSI/s1600-h/e40fa989ec0f3a7ed3e0bdf6f081dd69.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 266px; height: 42px;" src="http://3.bp.blogspot.com/_rh0QblLk0C0/SjFcIr_FWZI/AAAAAAAAAFE/7iGK83rQpSI/s400/e40fa989ec0f3a7ed3e0bdf6f081dd69.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5346155536981514642" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;An implementation of this function (&lt;tt&gt;betainc(a,b,x1,x2)&lt;/tt&gt;) is now available in mpmath trunk. I wrote the basics of this implementation a while back, but it was nearly useless without the recent upgrades to 2F1. Evaluating the incomplete beta function with various choices of parameters proved useful to identify and fix some corner cases in 2F1.&lt;br /&gt;&lt;br /&gt;One important application of the incomplete beta integral is that, when regularized, it is the cumulative distribution function of the beta distribution. As a sanity check, the following code successfully reproduces the plot of several beta CDF:s on the &lt;a href="http://en.wikipedia.org/wiki/Beta_distribution"&gt;Wikipedia page for the beta distribution&lt;/a&gt; (I even got the same colors!):&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;def B(a,b):&lt;br /&gt;    return lambda t: betainc(a,b,0,t,regularized=True)&lt;br /&gt;&lt;br /&gt;plot([B(1,3),B(0.5,0.5),B(5,1),B(2,2),B(2,5)], [0,1])&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_rh0QblLk0C0/SjEzXUeqzZI/AAAAAAAAAEs/KBuNEbsqB1I/s1600-h/betainc.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 400px; height: 302px;" src="http://3.bp.blogspot.com/_rh0QblLk0C0/SjEzXUeqzZI/AAAAAAAAAEs/KBuNEbsqB1I/s400/betainc.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5346110708392840594" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;The &lt;tt&gt;betainc&lt;/tt&gt; function is superior to manual numerical integration because of the numerically hairy singularities that occur at x = 0 and x = 1 for some choices of parameters. Thanks to having a good 2F1 implementation, &lt;tt&gt;betainc&lt;/tt&gt; gives accurate results even in those cases.&lt;br /&gt;&lt;br /&gt;The &lt;tt&gt;betainc&lt;/tt&gt; function also provides an appropriate analytic continuation of the beta integral, internally via the analytic continuation of 2F1. Thus the beta integral can be evaluated outside of the standard interval [0,1]; for parameters where the integrand is singular at 0 or 1, this is in the sense of a contour that avoids the singularity.&lt;br /&gt;&lt;br /&gt;It is interesting to observe how the integration introduces branch cuts; for example, in the following plot, you can see that 0 is a branch point when the first parameter is fractional and 1 is a branch point when the second parameter is fractional (when both are positive integers, the beta integral is just a polynomial, so it then behaves nicely):&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;# blue, red, green&lt;br /&gt;plot([B(2.5,2), B(3,1.5), B(3,2)], [-0.5,1.5], [-0.5,1.5])&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_rh0QblLk0C0/SjEzXqiN-eI/AAAAAAAAAE0/aUUR42WziiM/s1600-h/betacont.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 400px; height: 302px;" src="http://3.bp.blogspot.com/_rh0QblLk0C0/SjEzXqiN-eI/AAAAAAAAAE0/aUUR42WziiM/s400/betacont.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5346110714313308642" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;To check which integration path &lt;tt&gt;betainc&lt;/tt&gt; "uses", we can compare with numerical integration. For example, to integrate from 0 to 1.5, we can choose a contour that passes through +i (in the upper half plane) or -i (in the lower half plane):&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;&gt;&gt;&gt; mp.dps = 25&lt;br /&gt;&gt;&gt;&gt; print betainc(3, 1.5, 0, 1.5)&lt;br /&gt;(0.152380952380952380952381 + 0.4023774302466306150757186j)&lt;br /&gt;&gt;&gt;&gt; print quad(lambda x: x**2*(1-x)**0.5, [0, j, 1.5])&lt;br /&gt;(0.152380952380952380952381 - 0.4023774302466306150757186j)&lt;br /&gt;&gt;&gt;&gt; print quad(lambda x: x**2*(1-x)**0.5, [0, -j, 1.5])&lt;br /&gt;(0.152380952380952380952381 + 0.4023774302466306150757186j)&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;The sign of the imaginary part shows that &lt;tt&gt;betainc&lt;/tt&gt; gives the equivalent of a contour through the lower half plane. The convention turns out to agree with that used by Mathematica:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;In[10]:= Beta[0, 1.5, 3, 1.5]&lt;br /&gt;Out[10]= 0.152381 + 0.402377 I&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;I'll round things up by noting that I've also implemented the &lt;a href="http://mathworld.wolfram.com/En-Function.html"&gt;generalized exponential integral&lt;/a&gt; (the En-function) in mpmath as &lt;tt&gt;expint(n,z)&lt;/tt&gt;. A sample:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;&gt;&gt;&gt; print expint(2, 3.5)&lt;br /&gt;0.005801893920899125522331056&lt;br /&gt;&gt;&gt;&gt; print quad(lambda t: exp(-3.5*t)/t**2, [1,inf])&lt;br /&gt;0.005801893920899125522331056&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;The En-function is based on the incomplete gamma function, which is based on the hypergeometric series 1F1. These functions are still slow and/or inaccurate for certain arguments (in particular, for large ones), so they will require improvements along the lines of those for 2F1. Stay tuned for progress.&lt;br /&gt;&lt;br /&gt;In other news, mpmath 0.12 should be in both SymPy and Sage soon. With this announcement I'm just looking for an excuse to tag this post with both 'sympy' and 'sage' so it will show up on both Planet SymPy and Planet Sage :-) Posts purely about mpmath development should be relevant to both audiences though, I hope.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4754734402679928849-2181422470258036293?l=fredrik-j.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://fredrik-j.blogspot.com/feeds/2181422470258036293/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=4754734402679928849&amp;postID=2181422470258036293' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4754734402679928849/posts/default/2181422470258036293'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4754734402679928849/posts/default/2181422470258036293'/><link rel='alternate' type='text/html' href='http://fredrik-j.blogspot.com/2009/06/hypergeometric-2f1-incomplete-beta.html' title='Hypergeometric 2F1, incomplete beta, exponential integrals'/><author><name>Fredrik Johansson</name><uri>http://www.blogger.com/profile/01465400860530971858</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://3.bp.blogspot.com/_rh0QblLk0C0/SjFcIr_FWZI/AAAAAAAAAFE/7iGK83rQpSI/s72-c/e40fa989ec0f3a7ed3e0bdf6f081dd69.png' height='72' width='72'/><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4754734402679928849.post-4086527703850611398</id><published>2009-06-09T21:20:00.004+02:00</published><updated>2009-06-09T21:30:15.368+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='sage'/><category scheme='http://www.blogger.com/atom/ns#' term='sympy'/><category scheme='http://www.blogger.com/atom/ns#' term='mpmath'/><title type='text'>Mpmath 0.12 released</title><content type='html'>I'll quote the &lt;a href="http://groups.google.com/group/mpmath/browse_thread/thread/61c99dcd498a22"&gt;mailing list announcement&lt;/a&gt;:&lt;br /&gt;&lt;blockquote&gt;&lt;br /&gt;Mpmath version 0.12 is now available from the website:&lt;br /&gt;&lt;a href="http://code.google.com/p/mpmath/"&gt;http://code.google.com/p/mpmath/&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;It can also be downloaded from the Python Package Index:&lt;br /&gt;&lt;a href="http://pypi.python.org/pypi/mpmath/0.12"&gt;http://pypi.python.org/pypi/mpmath/0.12&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Mpmath is a pure-Python library for arbitrary-precision floating-point arithmetic that implements an extensive set of mathematical functions. It can be used as a standalone library or via SymPy (&lt;a href="http://code.google.com/p/sympy/"&gt;http://code.google.com/p/sympy/&lt;/a&gt;).&lt;br /&gt;&lt;br /&gt;Version 0.12 mostly contains bug fixes and speed improvements. New features include various special functions from analytic number theory, Newton's method as an option for root-finding, and more versatile printing of intervals. It is now also possible to create multiple working contexts each with its own precision. Finally, mpmath now recognizes being installed in Sage and will automatically wrap Sage's fast integer arithmetic if available.&lt;br /&gt;&lt;br /&gt;For more details, see the changelog:&lt;br /&gt;&lt;a href="http://mpmath.googlecode.com/svn/tags/0.12/CHANGES"&gt;http://mpmath.googlecode.com/svn/tags/0.12/CHANGES&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Bug reports and other comments are welcome at the issue tracker at&lt;br /&gt;&lt;a href="http://code.google.com/p/mpmath/issues/list"&gt;http://code.google.com/p/mpmath/issues/list&lt;/a&gt; or the mpmath mailing list:&lt;br /&gt;&lt;a href="http://groups.google.com/group/mpmath"&gt;http://groups.google.com/group/mpmath&lt;/a&gt; &lt;/blockquote&gt;&lt;br /&gt;&lt;br /&gt;I've previously blogged about some of the new features:&lt;br /&gt;&lt;br /&gt;&lt;a href="http://fredrik-j.blogspot.com/2009/03/computing-generalized-bell-numbers.html"&gt;Computing generalized Bell numbers&lt;/a&gt;&lt;br /&gt;&lt;a href="http://fredrik-j.blogspot.com/2009/02/approximate-prime-counting.html"&gt;Approximate prime counting&lt;/a&gt;&lt;br /&gt;&lt;a href="http://fredrik-j.blogspot.com/2009/02/fun-with-zeta-functions.html"&gt;Fun with zeta functions&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;See those posts or the documentation for many examples.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4754734402679928849-4086527703850611398?l=fredrik-j.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://fredrik-j.blogspot.com/feeds/4086527703850611398/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=4754734402679928849&amp;postID=4086527703850611398' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4754734402679928849/posts/default/4086527703850611398'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4754734402679928849/posts/default/4086527703850611398'/><link rel='alternate' type='text/html' href='http://fredrik-j.blogspot.com/2009/06/mpmath-012-released.html' title='Mpmath 0.12 released'/><author><name>Fredrik Johansson</name><uri>http://www.blogger.com/profile/01465400860530971858</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4754734402679928849.post-885151068335711281</id><published>2009-06-06T17:22:00.004+02:00</published><updated>2009-06-06T17:52:05.964+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='sage'/><category scheme='http://www.blogger.com/atom/ns#' term='mpmath'/><category scheme='http://www.blogger.com/atom/ns#' term='cython'/><title type='text'>Cython mpmath performance, part 2</title><content type='html'>A followup to the previous post. Turns out I had left a temporary integer variable undeclared, which slowed things down unnecessarily. After adding a simple cdef, the code runs a bit faster: at 53 bits, an addition now takes 850 ns, a multiplication 920 ns; a complex addition takes 1.25 µs and a complex multiplication takes 2.15 µs.&lt;br /&gt;&lt;br /&gt;I've also implemented division and square root. To continue the benchmarking started in the previous post:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;Real division, x/y&lt;br /&gt;&lt;br /&gt;       mpmath    cython    sage&lt;br /&gt;&lt;br /&gt;53     23.7 µs   1.49 µs   854 ns&lt;br /&gt;100    24.6 µs   1.84 µs   1.17 µs&lt;br /&gt;333    27 µs     2.69 µs   2.1 µs&lt;br /&gt;3333   70.3 µs   44 µs     42.8 µs&lt;br /&gt;&lt;br /&gt;Real square root, x.sqrt()&lt;br /&gt;&lt;br /&gt;53     21.1 µs   1.75 µs   1.48 µs&lt;br /&gt;100    22.5 µs   2.23 µs   1.68 µs&lt;br /&gt;333    32 µs     3.81 µs   3.11 µs&lt;br /&gt;3333   65.2 µs   35.3 µs   33.1 µs&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Division is a bit slow compared Sage, but it might be possible to improve. As with the other arithmetic operations, the speedup vs ordinary mpmath is still more than a factor 10 at low precision.&lt;br /&gt;&lt;br /&gt;Further, here is a benchmark of the new gamma function algorithm (mentioned in my talk at SD15), which is an order of magnitude faster than the algorithms currently used by both mpmath and MPFR. I had already written a pure Python implementation, which was quite fast, but it's insanely fast in Cython -- at really low precision it's even faster than the exponential function (although this fact probably indicates that the exponential function could be streamlined further). It's still half as fast as exp at 333-bit precision:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;Real gamma function, x.gamma()   (gamma(x) in mpmath)&lt;br /&gt;&lt;br /&gt;       mpmath    cython    sage&lt;br /&gt;&lt;br /&gt;53     332 µs    8.6 µs    195 µs&lt;br /&gt;100    434 µs    12.9 µs   257 µs&lt;br /&gt;333    1.63 ms   52 µs     975 µs&lt;br /&gt;3333   90.9 ms   9.95 ms   347 ms&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Yes, that is a whopping 20,000 hundred-digit gamma functions per second!&lt;br /&gt;&lt;br /&gt;Making the Cython code fully usable as an mpmath backend might require another week or two of work. This estimate includes rewriting utility code in Cython, but not porting transcendental functions. (The main reason why this will take longer is that I don't just want to translate the existing code, but redo the implementations from scratch to make everything uniformly faster.)&lt;br /&gt;&lt;br /&gt;I'm not working full time on Cython mpmath backend; in parallel, I'm working on the mpmath wrapper code for Sage (some of which is ready and has been posted to the Sage trac) and on high-level implementations of various special functions. The new functions I have written partial implementations for so far include the generalized exponential integral (E&lt;sub&gt;n&lt;/sub&gt;), incomplete beta function, Hurwitz zeta function with derivatives, Appell hypergeometric functions of two variables, and matrix sqrt/exp/log/cos/sin. If anyone feels that their favorite special function is missing from Sage and/or mpmath, I'm taking requests :-)&lt;br /&gt;&lt;br /&gt;I've also done little bit of work fixing hypergeometric functions for the hard cases, but really haven't made much progress on this so far (this is a substantial project which will take some time).&lt;br /&gt;&lt;br /&gt;By the way, there should be a new mpmath release any day now. I mostly need to review the documentation and do some minor cleanup.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4754734402679928849-885151068335711281?l=fredrik-j.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://fredrik-j.blogspot.com/feeds/885151068335711281/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=4754734402679928849&amp;postID=885151068335711281' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4754734402679928849/posts/default/885151068335711281'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4754734402679928849/posts/default/885151068335711281'/><link rel='alternate' type='text/html' href='http://fredrik-j.blogspot.com/2009/06/cython-mpmath-performance-part-2.html' title='Cython mpmath performance, part 2'/><author><name>Fredrik Johansson</name><uri>http://www.blogger.com/profile/01465400860530971858</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4754734402679928849.post-6096642037008723815</id><published>2009-05-27T12:56:00.004+02:00</published><updated>2009-05-27T14:17:03.120+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='sage'/><category scheme='http://www.blogger.com/atom/ns#' term='sympy'/><category scheme='http://www.blogger.com/atom/ns#' term='mpmath'/><category scheme='http://www.blogger.com/atom/ns#' term='cython'/><title type='text'>Cython mpmath performance</title><content type='html'>I'm presently working on a Cython-based backend for mpmath, with the immediate goal to make mpmath competitive as a component of Sage. The Cython code currently depends on utility functions in the Sage library but should eventually be possible to compile for standalone use or together with SymPy's future Cython backend (linking directly against GMP/MPIR).&lt;br /&gt;&lt;br /&gt;So far I've implemented a real type (mpf replacement) and a complex type (mpc replacement); addition, multiplication, and the real exponential function; just enough to do some basic benchmarking. Below is a comparison between standard Python mpmath (running on top of sage.Integer), the new Cython-based types, and Sage's RealNumber / ComplexNumber types. I used the inputs x = sqrt(3)-1, y = sqrt(5)-1, and for the complex cases z = x+yi, w = y+xi. The precision ranges between 53 bits (IEEE double compatible) and 3333 bits (&amp;asymp;1000 decimal digits).&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;Addition, x+y&lt;br /&gt;&lt;br /&gt;       mpmath    cython    sage&lt;br /&gt;&lt;br /&gt;53     8.72 µs   979 ns    707 ns&lt;br /&gt;100    8.81 µs   1.01 µs   733 ns&lt;br /&gt;333    10.1 µs   1.14 µs   737 ns&lt;br /&gt;3333   10.6 µs   1.59 µs   1.15 µs&lt;br /&gt;&lt;br /&gt;Multiplication, x*y&lt;br /&gt;&lt;br /&gt;53     9.04 µs   968 ns    728 ns&lt;br /&gt;100    9.56 µs   1.14 µs   901 ns&lt;br /&gt;333    11.3 µs   1.59 µs   1.2 µs&lt;br /&gt;3333   35.7 µs   25.4 µs   17.8 µs&lt;br /&gt;&lt;br /&gt;Real exponential function, exp(x)&lt;br /&gt;&lt;br /&gt;53     56.2 µs   9.41 µs   16 µs&lt;br /&gt;100    52.8 µs   11.6 µs   11.3 µs&lt;br /&gt;333    122 µs    25.4 µs   26.9 µs&lt;br /&gt;3333   1.38 ms   831 µs    1.14 ms&lt;br /&gt;&lt;br /&gt;Complex addition, z+w&lt;br /&gt;&lt;br /&gt;53    14.7 µs   1.45 µs   978 ns&lt;br /&gt;100   14.5 µs   1.7 µs    1 µs&lt;br /&gt;333   16.3 µs   1.85 µs   992 ns&lt;br /&gt;3333  17.7 µs   3.2 µs    1.73 µs&lt;br /&gt;&lt;br /&gt;Complex multiplication, z*w&lt;br /&gt;&lt;br /&gt;53    35.8 µs   2.72 µs   1.68 µs&lt;br /&gt;100   34.5 µs   2.76 µs   2.08 µs&lt;br /&gt;333   43.2 µs   4.38 µs   3.61 µs&lt;br /&gt;3333  139 µs    98.6 µs   70 µs&lt;br /&gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;A few observations can be made. First off, basic arithmetic with number instances is an order of magnitude faster when fully Cythonized. This isn't surprising because something as simple as an addition of two mpmath.mpf instances has to go through some 50 lines of Python code to check types, check for special cases, and round the result (creating lots of temporary objects, etc).&lt;br /&gt;&lt;br /&gt;Reimplemented in Cython, arithmetic comes well within half the speed of Sage's numbers. I believe MPFR (which Sage uses) does arithmetic faster because it works directly with the limb data and uses some tricks to speed up the rounding, whereas my implementation uses the mpz interface straightforwardly. Some difference also comes from the fact that MPFR uses machine-precision integers for exponents, whereas I'm using mpz_t to allow arbitrary-size exponents. In any case, the results are very good.&lt;br /&gt;&lt;br /&gt;At very low precision, memory allocations account for probably half the time (for both my Cython-based types and Sage's RealNumber). Thus there is some room for improvement later on by implementing a freelist.&lt;br /&gt;&lt;br /&gt;The best news (for special functions) is that my exponential function is as fast as MPFR's, so the same should be true for other functions when I get there. The new exp (which uses a slightly different algorithm from the one currently in mpmath) is actually slightly faster than MPFR, although it should be said that performance for transcendental functions depends heavily on tuning, the inputs, and possibly other factors (I have no idea why Sage's RealNumber.exp in my benchmark is much slower at 53 bits than at 100 bits, for example), so this is not a conclusive result.&lt;br /&gt;&lt;br /&gt;The code itself is currently too messy and ad-hoc to share publicly, sorry.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4754734402679928849-6096642037008723815?l=fredrik-j.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://fredrik-j.blogspot.com/feeds/6096642037008723815/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=4754734402679928849&amp;postID=6096642037008723815' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4754734402679928849/posts/default/6096642037008723815'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4754734402679928849/posts/default/6096642037008723815'/><link rel='alternate' type='text/html' href='http://fredrik-j.blogspot.com/2009/05/cython-mpmath-performance.html' title='Cython mpmath performance'/><author><name>Fredrik Johansson</name><uri>http://www.blogger.com/profile/01465400860530971858</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4754734402679928849.post-533947463395764920</id><published>2009-05-22T06:21:00.007+02:00</published><updated>2009-06-01T15:53:03.787+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='sage'/><category scheme='http://www.blogger.com/atom/ns#' term='mpmath'/><category scheme='http://www.blogger.com/atom/ns#' term='sage days'/><title type='text'>Report from Sage Days 15</title><content type='html'>&lt;a href="http://wiki.sagemath.org/days15"&gt;Sage Days 15&lt;/a&gt; is now over. I'm still in Seattle as I write this; I depart early tomorrow. It's been a great week in almost every way. I didn't take photos, but see &lt;a href="http://wstein.org/sagedays15/"&gt;William Stein's photos and video recordings&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;&lt;i&gt;Edit&lt;/i&gt;: David Joyner also has photos:&lt;br /&gt;&lt;a href="http://sage.math.washington.edu/home/wdj/sagedays/sd15/"&gt;http://sage.math.washington.edu/home/wdj/sagedays/sd15/&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;h3&gt;People&lt;/h3&gt;&lt;br /&gt;My prejudices were confirmed insofar as that the Sage developers are an exceptionally friendly bunch. Everybody was helpful and had a good sense of humor. And of course, these people are all very good at what they work on. It's both humbling and inspiring to be surrounded by people who are far more knowledgeable than yourself about almost everything.&lt;br /&gt;&lt;br /&gt;There were a couple of talks daily (except for the last two days), most of which were very good; not to mention all the interesting informal discussions. Personally, I thought it was particularly interesting to get Bill Hart's (&lt;a href="http://www.mpir.org/"&gt;MPIR&lt;/a&gt; and &lt;a href="http://www.flintlib.org/"&gt;FLINT&lt;/a&gt; guy) perspective of his work; he also provided useful technical advice. William's presentation of the general direction of the Sage project and the &lt;a href="http://www.cython.org/"&gt;Cython&lt;/a&gt; talk by Robert Bradshaw and Craig Citro were also quite informative.&lt;br /&gt;&lt;br /&gt;I gave a short talk about mpmath on Monday (&lt;a href="http://wiki.sagemath.org/days15?action=AttachFile&amp;amp;do=view&amp;amp;target=johansson-mpmath.pdf"&gt;slides&lt;/a&gt;). I'm not sure if the talk itself was good, but several people seemed to be interested in my work. I've received questions and comments about mpmath all week, and the general attitute towards using it in Sage seems positive.&lt;br /&gt;&lt;br /&gt;&lt;h3&gt;Cython&lt;/h3&gt;&lt;br /&gt;On the purely technical side, by far the coolest thing about SD15 was the chance to finally learn Cython. I've seen Cython in action before, but never actually used it myself. My impressions are exclusively positive. I have plenty of experience with Python and a decent amount of experience with C, and Cython truly combines the best (alternatively, removes the worst) of both worlds. The biggest surprise (and of course this is Cython's selling point) is how simple the interfacing between high level and low level code becomes, and the fact that it is all very robust.&lt;br /&gt;&lt;br /&gt;It's exiciting to see that there are several active projects around that attempt to speed up Python. The nice thing about Cython is that it doesn't give you "half the speed of C" or "maybe nearly the speed of C, 3 years from now" -- it gives the real deal, -O3 C, and it works right now. I hope Cython will be part of the standard Python distribution within a year or two from now, and thereby make it even easier to build Cython extensions, especially on Windows.&lt;br /&gt;&lt;br /&gt;On a tangential note, it's good to see that progress is being made on the native Windows port of Sage. (I dual boot between Windows and Ubuntu, and vmware Sage on Windows is plainly inconvenient.) The pragmatic attitude of various Sage developers towards issues such as support for proprietary platforms gives the impression of a project in good hands.&lt;br /&gt;&lt;br /&gt;&lt;h3&gt;Coding&lt;/h3&gt;&lt;br /&gt;I offered right before SD15 to look at speeding up the prime counting function in Sage by implementing the asymptotically fast Meissel-Lehmer-Lagarias-Miller-Odlyzko method. Doing this from scratch turned out to be unnecessary when Victor Miller emailed me the C implementation of the algorithm which he wrote two decades ago! The code compiles, and is amazingly fast on my laptop; unfortunately, it has some issues (such as not working for small n). It would be possible to wrap it in Sage as-is, and just use a different algorithm for small n. But what's probably an even better solution is that Victor himself (he showed up at SD) is now working on reimplementing his algorithm in Sage.&lt;br /&gt;&lt;br /&gt;Most of my productive coding time (I also spent quite a lot of time just poking around at the Sage codebase -- which I've never really looked at before) went into wrapping mpmath for Sage. The purpose of wrapping mpmath is mainly to improve the support for special functions in Sage, firstly by just making the mpmath function library available and secondly by further improving the algorithms in mpmath. (I am getting funded via William's research grant to work on the latter this summer.)&lt;br /&gt;&lt;br /&gt;As a first step, I made mpmath able to use sage.Integer instead of Python long. This was mostly trivial, the only problem being a coercion-related bug in sage.Integer that had to be fixed (William helped me write the patch, my first for Sage!).&lt;br /&gt;&lt;br /&gt;When all mpmath tests finally passed, they unfortunately turned out to run 40% times slower than pure Python and 2.6x (!) slower than gmpy mpmath. The slowdown was in fact expected since sage.Integer was known to be substantially slower than gmpy.mpz. I therefore helped Robert Bradshaw identify the performance bottlenecks (they were mostly due either to unnecessary coercions or unnecessary memory reallocation), and he promptly patched them up. A nice side effect is that many parts of Sage should be faster now, since much code depends on the Integer type.&lt;br /&gt;&lt;br /&gt;After I also implemented a few exra helper functions in Cython, mpmath on top of Sage now runs just a few percent slower than gmpy mpmath. Much of the remaining difference is probably due to inefficiencies in my own Cython code.&lt;br /&gt;&lt;br /&gt;Finally, I also wrote wrapper code in Cython that permits Sage to call mpmath functions with Sage objects as input and getting Sage RealNumber or ComplexNumber back, all with reasonably low conversion overhead. This should now make it trivial to fix the state of numerical special functions in Sage (wrapping an mpmath function is literally a one-liner). I'm just waiting for Sage 4.0 with the new symbolics code to come out before I submit a patch and start adding wrapped functions; I want to ensure that the symbolics and numerics integrate well.&lt;br /&gt;&lt;br /&gt;The state of mpmath is that it is still a bit slow compared to some numerical functions in Sage, but it's not at all bad. The long term plan is to speed up mpmath further by providing a pure-Cython backend, which along with certain algorithmic improvements should make it roughly as fast as MPFR and Pari for cases where it's currently slower.&lt;br /&gt;&lt;br /&gt;&lt;h3&gt;Travel&lt;/h3&gt;&lt;br /&gt;The flight over here was reasonably convenient considering the circa 20-hour duration with transfers accounted for (the repeated security checks were really the biggest hassle). Hopefully the flight back tomorrow will proceed as smoothly.&lt;br /&gt;&lt;br /&gt;Seattle, at least the University District, is a fairly nice city (not least because of the trees everywhere). Except for the trip to Microsoft Research on Tuesday, all activities took place&lt;br /&gt;at the University of Washington campus. The campus alone is huge, so there has been plenty to see. I've lived very conveniently at the College Inn, right next to both the campus and The Ave.&lt;br /&gt;&lt;br /&gt;I did make one excursion to downtown Seattle. I arrived early last Friday and with nothing on the schedule that day, I had time to meet up with Huy Pham (fellow &lt;a href="http://www.doomworld.com/"&gt;Doomer&lt;/a&gt; and online acquintance) and we went to see Star Trek in IMAX (at the Pacific Science Center), which was well worth the admission.&lt;br /&gt;&lt;br /&gt;As a last remark, I'm very grateful to William and the other Sage Days 15 organizers for their good job and for inviting me (and paying for my trip!). I've had a great time, had the opportunity meet interesting people, learned many things, and now I'm sad it's over.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4754734402679928849-533947463395764920?l=fredrik-j.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://fredrik-j.blogspot.com/feeds/533947463395764920/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=4754734402679928849&amp;postID=533947463395764920' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4754734402679928849/posts/default/533947463395764920'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4754734402679928849/posts/default/533947463395764920'/><link rel='alternate' type='text/html' href='http://fredrik-j.blogspot.com/2009/05/report-from-sage-days-15.html' title='Report from Sage Days 15'/><author><name>Fredrik Johansson</name><uri>http://www.blogger.com/profile/01465400860530971858</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4754734402679928849.post-4550877639959394847</id><published>2009-03-04T19:48:00.006+01:00</published><updated>2009-03-04T21:49:54.981+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='sympy'/><category scheme='http://www.blogger.com/atom/ns#' term='mpmath'/><category scheme='http://www.blogger.com/atom/ns#' term='math'/><title type='text'>Computing (generalized) Bell numbers</title><content type='html'>Over the last few days, I procrastinated a bit from more important tasks by &lt;a href="http://code.google.com/p/mpmath/source/detail?r=894"&gt;implementing&lt;/a&gt; Bell numbers (and more generally, evaluation of &lt;a href="http://mathworld.wolfram.com/BellPolynomial.html"&gt;Bell polynomials&lt;/a&gt;) in mpmath. The Bell polynomials are defined by &lt;i&gt;B&lt;/i&gt;&lt;sub&gt;0&lt;/sub&gt;(&lt;i&gt;x&lt;/i&gt;) = 1 and&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://upload.wikimedia.org/math/d/9/8/d9830e1823a610bff070cbffb0ed5e95.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 258px; height: 51px;" src="http://upload.wikimedia.org/math/d/9/8/d9830e1823a610bff070cbffb0ed5e95.png" border="0" alt="" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;In particular, &lt;i&gt;B&lt;sub&gt;n&lt;/sub&gt;(1)&lt;/i&gt; is the &lt;i&gt;n&lt;/i&gt;th Bell number, which has combinatorial significance as the number of partitions of a set with &lt;i&gt;n&lt;/i&gt; elements.&lt;br /&gt;&lt;br /&gt;Instead of using the recurrence formula above, I implemented an approximate formula (discussed below) to provide fast approximate evaluation. So it is for example possible to do:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;&gt;&gt;&gt; from mpmath import *&lt;br /&gt;&gt;&gt;&gt; mp.dps = 30&lt;br /&gt;&gt;&gt;&gt; print bell(100000)&lt;br /&gt;1.04339424254293899845402468388e+364471&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;This algorithms turns out to work well for exact evaluation at integer arguments, by setting the precision large enough, e.g. like this:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;&gt;&gt;&gt; from mpmath import *&lt;br /&gt;&gt;&gt;&gt; from mpmath.functions import funcwrapper&lt;br /&gt;&gt;&gt;&gt;&lt;br /&gt;&gt;&gt;&gt; @funcwrapper&lt;br /&gt;... def bellexact(n,x=1):&lt;br /&gt;...     mp.prec = 20&lt;br /&gt;...     size = int(log(bell(n,x),2))+10&lt;br /&gt;...     mp.prec = size&lt;br /&gt;...     return int(bell(n,x)+0.5)&lt;br /&gt;...&lt;br /&gt;&gt;&gt;&gt; bellexact(100)&lt;br /&gt;47585391276764833658790768841387207826363669686825611466616334637559114&lt;br /&gt;622672724044217756306953557882560751L&lt;br /&gt;&gt;&gt;&gt;&lt;br /&gt;&gt;&gt;&gt; bellexact(50)&lt;br /&gt;185724268771078270438257767181908917499221852770L&lt;br /&gt;&gt;&gt;&gt; bellexact(40,40)&lt;br /&gt;5288533501514377257070614176831982123270530650242136500078910824523240L&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;I did a bit of benchmarking for computing large Bell numbers &lt;i&gt;B&lt;sub&gt;n&lt;/sub&gt;&lt;/i&gt;, and this approach appears to be much faster than what other systems use. Below are my timings for &lt;a href="http://code.google.com/p/sympy/"&gt;SymPy&lt;/a&gt;, &lt;a href="http://www.gap-system.org/"&gt;GAP&lt;/a&gt;, &lt;a href="http://www.wolfram.com/mathematica/"&gt;Mathematica&lt;/a&gt;, and mpmath. (Note: as I do not have a personal Mathematica license, I ran it remotely on a different system, which based on a comparison of integer multiplication speed is 10-20% faster than my own laptop.)&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;      n        SymPy      GAP   Mathematica    mpmath&lt;br /&gt;    100      0.007 s      0 s       0.005 s   0.012 s&lt;br /&gt;    1000       8.9 s   0.78 s        0.32 s    0.30 s&lt;br /&gt;    3000           -     67 s          12 s     3.6 s&lt;br /&gt;    10000          -        -         396 s      76 s&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;From what I've read, Bell numbers have some interesting number-theoretical properties such as &lt;a href="http://mathworld.wolfram.com/TouchardsCongruence.html"&gt;Touchard's congruence&lt;/a&gt;, so this could potentially be useful to someone. (Interestingly, I wonder if Touchard's congruence could be turned into an even faster modular algorithm for Bell numbers, similar to &lt;a href="http://cims.nyu.edu/~harvey/"&gt;David Harvey's&lt;/a&gt; &lt;a href="http://arxiv.org/abs/0807.1347"&gt;algorithm for Bernoulli numbers&lt;/a&gt;.)&lt;br /&gt;&lt;br /&gt;The formula I implemented for the Bell polynomials is the infinite series&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://upload.wikimedia.org/math/c/2/9/c29b1e033fdd5e5af4eda8c6b0ef218d.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 181px; height: 48px;" src="http://upload.wikimedia.org/math/c/2/9/c29b1e033fdd5e5af4eda8c6b0ef218d.png" border="0" alt="" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;which is known by the name &lt;i&gt;&lt;a href="http://mathworld.wolfram.com/DobinskisFormula.html"&gt;Dobinski's formula&lt;/a&gt;&lt;/i&gt;.&lt;br /&gt;&lt;br /&gt;As a side note, I implemented this series using a piece of experimental new summation code designed to ensure full accuracy in spite of cancellation. For example, the following evaluation involves alternating terms of magnitude over 10&lt;sup&gt;400&lt;/sup&gt;; the internal precision is automatically set to over 400 digits to provide the requested 15 digits:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;&gt;&gt;&gt; mp.dps = 15&lt;br /&gt;&gt;&gt;&gt; print bell(10,-1000)&lt;br /&gt;9.55744142784509e+29&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;I'll probably be working more this summer on implementing similar methods for several mpmath functions that are currently implemented somewhat less robustly (more on that some other time).&lt;br /&gt;&lt;br /&gt;One interesting aspect of Dobinski's formula is that it generalizes to arbitrary complex values for &lt;i&gt;n&lt;/i&gt;, not just integers. I always prefer to implement functions for the most general possible arguments (and will sometimes hesitate to implement a function if I can only do it for a special case). Generalization is extremely useful: extending functions defined on the integers to the reals permits the use of differentiation, continuous optimization algorithms, etc. Extending further to the complex numbers allows for contour integration. Curiously, Mathematica does not seem to do this for its &lt;a href="http://reference.wolfram.com/mathematica/ref/BellB.html"&gt;BellB&lt;/a&gt; function.&lt;br /&gt;&lt;br /&gt;Unfortunately, there is a difficulty in implementing Dobinski's formula for general &lt;i&gt;n&lt;/i&gt;: the 0th term is singular unless &lt;i&gt;n&lt;/i&gt; is a nonnegative real number. Removing the term completely breaks compatibility with the standard definition of the Bell polynomials for &lt;i&gt;n&lt;/i&gt; = 0. Keeping it either makes the series undefined, or at best renders the function discontinuous at the single point &lt;i&gt;n&lt;/i&gt; = 0 which is certainly not desirable. As a workaround, I changed the formula to&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://upload.wikimedia.org/math/2/f/e/2fe8b4e5768cc4c3b2884cab699317c0.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 308px; height: 60px;" src="http://upload.wikimedia.org/math/2/f/e/2fe8b4e5768cc4c3b2884cab699317c0.png" border="0" alt="" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;The sinc term is continuous (analytic), and zero when &lt;i&gt;n&lt;/i&gt; is an integer except when &lt;i&gt;n&lt;/i&gt; = 0, so it solves all problems. The sinc function has no connection to Bell polynomials whatsoever as far as I know, but it is the simplest possible "patch" I can think of.&lt;br /&gt;&lt;br /&gt;Voila, we now get a smooth transition between for example &lt;i&gt;B&lt;/i&gt;&lt;sub&gt;0&lt;/sub&gt;(&lt;i&gt;x&lt;/i&gt;) = 0 and &lt;i&gt;B&lt;/i&gt;&lt;sub&gt;1&lt;/sub&gt;(&lt;i&gt;x&lt;/i&gt;) = &lt;i&gt;x&lt;/i&gt;:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;&gt;&gt;&gt; f = lambda k: (lambda x: bell(k,x))&lt;br /&gt;&gt;&gt;&gt; plot([f(k) for k in linspace(0,1,5)], [-2,3])&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://1.bp.blogspot.com/_rh0QblLk0C0/Sa7lVmlem_I/AAAAAAAAAEk/jfa2k1uO5ek/s1600-h/transition.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 400px; height: 302px;" src="http://1.bp.blogspot.com/_rh0QblLk0C0/Sa7lVmlem_I/AAAAAAAAAEk/jfa2k1uO5ek/s400/transition.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5309433170014149618" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;It also becomes possible to e.g. compute a Taylor series of &lt;i&gt;B&lt;/i&gt; seen as a function of &lt;i&gt;n&lt;/i&gt;, using either a step sum or complex integration for the derivatives:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;&gt;&gt;&gt; nprint(chop(taylor(lambda n: bell(n,1), 0, 5, method='step')))&lt;br /&gt;[1.0, 0.222119, -0.504269, 3.32909e-2, 0.307601, 2.09191e-3]&lt;br /&gt;&gt;&gt;&gt; nprint(chop(taylor(lambda n: bell(n,1), 0, 5, method='quad')))&lt;br /&gt;[1.0, 0.222119, -0.504269, 3.32909e-2, 0.307601]&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;We can for example use the Taylor expansion around &lt;i&gt;n&lt;/i&gt; = 0 to evaluate the Bell polynomial with &lt;i&gt;n&lt;/i&gt; = 1 (not really practical for anything, but a beautiful example of the power of analysis):&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;&gt;&gt;&gt; print polyval(taylor(lambda n: bell(n,3), 0, 5)[::-1], 1)&lt;br /&gt;2.9949024096858&lt;br /&gt;&gt;&gt;&gt; print polyval(taylor(lambda n: bell(n,3), 0, 10)[::-1], 1)&lt;br /&gt;2.99998868106305&lt;br /&gt;&gt;&gt;&gt; print polyval(taylor(lambda n: bell(n,3), 0, 15)[::-1], 1)&lt;br /&gt;2.99999998751065&lt;br /&gt;&gt;&gt;&gt; print polyval(taylor(lambda n: bell(n,3), 0, 25)[::-1], 1)&lt;br /&gt;3.0&lt;br /&gt;&gt;&gt;&gt; print bell(1,3)&lt;br /&gt;3.0&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Finally, I also implemented the raw series&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://upload.wikimedia.org/math/5/8/5/58568727be008496b7d7a0ad327f7c93.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 140px; height: 49px;" src="http://upload.wikimedia.org/math/5/8/5/58568727be008496b7d7a0ad327f7c93.png" border="0" alt="" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;as a separate function, because it seems like it could be useful in its own right. Unfortunately, this function does not appear to have a standardized notation. For certain special values, it reduces to a Bell polynomial times an exponential function; an incomplete gamma function; or a generalized hypergeometric function. It is also related to the Poisson distribution. Other than that, I have not found much information about it. Does this function have a name? I named it &lt;tt&gt;polyexp&lt;/tt&gt; in mpmath, because it can be seen as an exponential analog of the polylogarithm.&lt;br /&gt;&lt;br /&gt;As a function of &lt;i&gt;s&lt;/i&gt;, it is an L-series, and shows interesting, highly chaotic behavior in the complex plane:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;&gt;&gt;&gt; mp.dps = 5&lt;br /&gt;&gt;&gt;&gt; cplot(lambda s: polyexp(s,1), [-20,20],[0,60], points=50000)&lt;br /&gt;&gt;&gt;&gt; plot(lambda s: polyexp(s*j,1), [0,60])&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_rh0QblLk0C0/Sa7T3dzK_dI/AAAAAAAAAEU/T3jVlwyLzzg/s1600-h/polyexp.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 400px; height: 302px;" src="http://4.bp.blogspot.com/_rh0QblLk0C0/Sa7T3dzK_dI/AAAAAAAAAEU/T3jVlwyLzzg/s400/polyexp.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5309413960561917394" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://1.bp.blogspot.com/_rh0QblLk0C0/Sa7T6CF0fxI/AAAAAAAAAEc/X5N52wZHvHM/s1600-h/polyexp_imag.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 400px; height: 302px;" src="http://1.bp.blogspot.com/_rh0QblLk0C0/Sa7T6CF0fxI/AAAAAAAAAEc/X5N52wZHvHM/s400/polyexp_imag.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5309414004663549714" /&gt;&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4754734402679928849-4550877639959394847?l=fredrik-j.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://fredrik-j.blogspot.com/feeds/4550877639959394847/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=4754734402679928849&amp;postID=4550877639959394847' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4754734402679928849/posts/default/4550877639959394847'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4754734402679928849/posts/default/4550877639959394847'/><link rel='alternate' type='text/html' href='http://fredrik-j.blogspot.com/2009/03/computing-generalized-bell-numbers.html' title='Computing (generalized) Bell numbers'/><author><name>Fredrik Johansson</name><uri>http://www.blogger.com/profile/01465400860530971858</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://1.bp.blogspot.com/_rh0QblLk0C0/Sa7lVmlem_I/AAAAAAAAAEk/jfa2k1uO5ek/s72-c/transition.png' height='72' width='72'/><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4754734402679928849.post-2577870337778486365</id><published>2009-02-21T12:40:00.007+01:00</published><updated>2009-02-21T17:04:45.714+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='sympy'/><category scheme='http://www.blogger.com/atom/ns#' term='mpmath'/><category scheme='http://www.blogger.com/atom/ns#' term='python'/><category scheme='http://www.blogger.com/atom/ns#' term='math'/><title type='text'>How (not) to compute harmonic numbers</title><content type='html'>The &lt;i&gt;n&lt;/i&gt;th &lt;a href="http://en.wikipedia.org/wiki/Harmonic_number"&gt;harmonic number&lt;/a&gt; is the &lt;i&gt;n&lt;/i&gt;th partial sum of the divergent harmonic series, &lt;i&gt;H&lt;sub&gt;n&lt;/sub&gt;&lt;/i&gt; = 1 + 1/2 + 1/3 + ... + 1/&lt;i&gt;n&lt;/i&gt;. The simplest way to compute this quantity is to add it directly the way it is written: 1, 1+1/2, 1+1/2+1/3, and so on. For &lt;i&gt;n&lt;/i&gt; approximately greater than 10 or 100, this is algorithm is not a very good one. Why?&lt;br /&gt;&lt;br /&gt;Firstly, in floating-point arithmetic, it is much better to use the asymptotic expansion&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://upload.wikimedia.org/math/0/5/1/0516d4c5d9a19f09ffcfc04a6a596928.png"&gt;&lt;img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer; width: 437px; height: 42px;" src="http://upload.wikimedia.org/math/0/5/1/0516d4c5d9a19f09ffcfc04a6a596928.png" alt="" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;which requires &lt;i&gt;fewer&lt;/i&gt; terms the larger &lt;i&gt;n&lt;/i&gt; is. Combining this with recurrence when &lt;i&gt;n&lt;/i&gt; is too small gives a fast numerical algorithm for fixed-precision or arbitrary-precision computation of harmonic numbers (especially large ones). It allows one to determine, for example, that it takes exactly 15092688622113788323693563264538101449859497 terms until the partial sums of the harmonic series exceed 100. (This algorithm is implemented in &lt;a href="http://code.google.com/p/mpmath/"&gt;mpmath&lt;/a&gt;, and most other comparable software.)&lt;br /&gt;&lt;br /&gt;Secondly, the naive algorithm is especially bad for &lt;i&gt;exact&lt;/i&gt; computation of harmonic numbers. This might not be obvious at a glance. We are just adding &lt;i&gt;n&lt;/i&gt; numbers: what's to improve? The catch is that rational arithmetic has hidden costs: a single addition requires three multiplications and usually a GCD reduction. In contrast with integer or floating-point addition, rational addition gets very slow when the numerators and denominators get large. Computing harmonic numbers the naive way triggers worst-case behavior of rational arithmetic: the denominators grow like &lt;i&gt;n&lt;/i&gt;! (if no GCD is performed, the denominator of &lt;i&gt;H&lt;sub&gt;n&lt;/sub&gt;&lt;/i&gt; literally becomes &lt;i&gt;n&lt;/i&gt;!).&lt;br /&gt;&lt;br /&gt;It should be well known that computing &lt;i&gt;n&lt;/i&gt;! as 1, 1·2, 1·2·3, ... is a poor method when &lt;i&gt;n&lt;/i&gt; is large. A much better algorithm, assuming that multiplication is subquadratic, is to recursively split the products in half, i.e. compute (1·2·...·n/2) · ((n/2+1)·...·n) and so on. This algorithm has complexity O(log(&lt;i&gt;n&lt;/i&gt;) &lt;i&gt;M&lt;/i&gt;(&lt;i&gt;n&lt;/i&gt; log(&lt;i&gt;n&lt;/i&gt;)) compared to O(&lt;i&gt;n&lt;/i&gt;&lt;sup&gt;2&lt;/sup&gt; log(&lt;i&gt;n&lt;/i&gt;)) for the naive algorithm, where &lt;i&gt;M&lt;/i&gt;(&lt;i&gt;n&lt;/i&gt;) is the complexity of multiplying two &lt;i&gt;n&lt;/i&gt;-bit integers. This approach is called &lt;a href="http://numbers.computation.free.fr/Constants/Algorithms/splitting.html"&gt;binary splitting&lt;/a&gt;, and has many other applications.&lt;br /&gt;&lt;br /&gt;The example of factorials directly suggests an analogous algorithm for computing harmonic numbers with reduced complexity: just recursively split the summation in half.&lt;br /&gt;&lt;br /&gt;A few more variations are also possible. Instead of working with rational numbers, which require a GCD reduction after each addition, one can work with the unreduced numerators and denominators as integers, and form a rational number at the end. Another way to obtain a pure-integer algorithm for &lt;i&gt;H&lt;sub&gt;n&lt;/sub&gt;&lt;/i&gt; = &lt;i&gt;p&lt;/i&gt;/&lt;i&gt;q&lt;/i&gt; is to compute the denominator &lt;i&gt;q&lt;/i&gt; = &lt;i&gt;n&lt;/i&gt;! and then use the formula&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://upload.wikimedia.org/math/9/4/4/9440a4d3ed24576b23f221fe2bed0521.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 87px; height: 49px;" src="http://upload.wikimedia.org/math/9/4/4/9440a4d3ed24576b23f221fe2bed0521.png" border="0" alt="" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;for the numerator.&lt;br /&gt;&lt;br /&gt;Below, I have implemented five different algorithms for computing harmonic numbers based on the preceding remarks. The code works with either Python 2.6, using the &lt;tt&gt;Fraction&lt;/tt&gt; type from the standard library, or with &lt;a href="http://code.google.com/p/gmpy/"&gt;gmpy&lt;/a&gt;, by adding the following respective header code:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;# python&lt;br /&gt;from math import factorial as fac&lt;br /&gt;from fractions import Fraction as mpq&lt;br /&gt;one = 1&lt;br /&gt;mpz = int&lt;br /&gt;&lt;br /&gt;# gmpy&lt;br /&gt;from gmpy import mpz, mpq, fac&lt;br /&gt;one = mpz(1)&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Algorithm 1: directly add 1, 1/2, 1/3, ..., 1/&lt;i&gt;n&lt;/i&gt;: &lt;pre&gt;&lt;br /&gt;def harmonic1(n):&lt;br /&gt;    return sum(mpq(1,k) for k in xrange(1,n+1))&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Algorithm 2: like algorithm 1, GCD reduction postponed to the end: &lt;pre&gt;&lt;br /&gt;def harmonic2(n):&lt;br /&gt;    p, q = mpz(1), mpz(1)&lt;br /&gt;    for k in xrange(2,n+1):&lt;br /&gt;        p, q = p*k+q, k*q&lt;br /&gt;    return mpq(p,q)&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Algorithm 3: compute denominator; compute numerator series: &lt;pre&gt;&lt;br /&gt;def harmonic3(n):&lt;br /&gt;    q = fac(n)&lt;br /&gt;    p = sum(q//k for k in xrange(1,n+1))&lt;br /&gt;    return mpq(p,q)&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Algorithm 4: binary splitting: &lt;pre&gt;&lt;br /&gt;def _harmonic4(a, b):&lt;br /&gt;    if b-a == 1:&lt;br /&gt;        return mpq(1,a)&lt;br /&gt;    m = (a+b)//2&lt;br /&gt;    return _harmonic4(a,m) + _harmonic4(m,b)&lt;br /&gt;&lt;br /&gt;def harmonic4(n):&lt;br /&gt;    return _harmonic4(1,n+1)&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Algorithm 5: binary splitting, GCD reduction postponed to the end: &lt;pre&gt;&lt;br /&gt;def _harmonic5(a, b):&lt;br /&gt;    if b-a == 1:&lt;br /&gt;        return one, mpz(a)&lt;br /&gt;    m = (a+b)//2&lt;br /&gt;    p, q = _harmonic5(a,m)&lt;br /&gt;    r, s = _harmonic5(m,b)&lt;br /&gt;    return p*s+q*r, q*s&lt;br /&gt;&lt;br /&gt;def harmonic5(n):&lt;br /&gt;    return mpq(*_harmonic5(1,n+1))&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;I did some benchmarking on my laptop (32-bit, 1.6 GHz Intel Celeron M), with &lt;i&gt;n&lt;/i&gt; up to 10&lt;sup&gt;4&lt;/sup&gt; for the Python version and 10&lt;sup&gt;6&lt;/sup&gt; for the gmpy version. Here are the results (times in seconds):&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;python:&lt;br /&gt;n         harmonic1  harmonic2  harmonic3  harmonic4  harmonic5&lt;br /&gt;10         0.000159   0.000009   0.000010   0.000160   0.000025&lt;br /&gt;100        0.004060   0.000233   0.000234   0.002211   0.000358&lt;br /&gt;1000       0.889937   0.028822   0.029111   0.048106   0.026434&lt;br /&gt;10000    769.109402   3.813702   3.823027   2.710409   3.475280&lt;br /&gt;&lt;br /&gt;gmpy:&lt;br /&gt;n         harmonic1  harmonic2  harmonic3  harmonic4  harmonic5&lt;br /&gt;10         0.000028   0.000010   0.000010   0.000033   0.000021&lt;br /&gt;100        0.000284   0.000097   0.000111   0.000365   0.000226&lt;br /&gt;1000       0.003543   0.001870   0.004986   0.004280   0.002651&lt;br /&gt;10000      0.103110   0.142100   0.588723   0.059249   0.052265&lt;br /&gt;100000     7.861546  17.002986  74.712460   1.115333   1.200865&lt;br /&gt;1000000  994.548226      -           -     27.927289  24.425421&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Algorithm 1 clearly does not do well asymptotically. For the largest &lt;i&gt;n&lt;/i&gt; measured, it is 284 times slower than the fastest algorithm with Python arithmetic and 40 times slower with gmpy arithmetic. Interestingly, algorithms 2 and 3 both substantially improve on algorithm 1 with Python arithmetic at least up to &lt;i&gt;n&lt;/i&gt; = 10000, but both are worse than algorithm 1 with gmpy. This is presumably because GMP uses a much better GCD algorithm that reduces the relative cost of rational arithmetic.&lt;br /&gt;&lt;br /&gt;Algorithms 4 and 5 are the clear winners for large &lt;i&gt;n&lt;/i&gt;, but it is hard to tell which is better. The timings seem to fluctuate a bit when repeated, so small differences in the table above are unreliable. I think algorithm 5 could be optimized a bit if implemented in another language, by accumulating temporary values in machine integers.&lt;br /&gt;&lt;br /&gt;It is amusing to note that computing the 10000th harmonic number with the recursive algorithm and an optimized implementation of rational arithmetic is &lt;b&gt;14,700 times faster&lt;/b&gt; than the direct algorithm with a non-optimized implementation of rational arithmetic. Choosing a larger &lt;i&gt;n&lt;/i&gt; (say, 10&lt;sup&gt;5&lt;/sup&gt;) gives an even more sensational ratio, of course, which I'm not patient enough to try. The moral is: Moore's law is not an excuse for doing it wrong.&lt;br /&gt;&lt;br /&gt;Algorithm 1 is not all bad, of course. With memoization (or implemented as a generator), it is good enough for sequential generation of the numbers &lt;i&gt;H&lt;/i&gt;&lt;sub&gt;1&lt;/sub&gt;, &lt;i&gt;H&lt;/i&gt;&lt;sub&gt;2&lt;/sub&gt;, &lt;i&gt;H&lt;/i&gt;&lt;sub&gt;3&lt;/sub&gt;, ..., and this tends to be needed much more commonly than isolated large harmonic numbers. &lt;a href="http://code.google.com/p/sympy/"&gt;SymPy&lt;/a&gt; therefore uses the memoized version of algorithm 1.&lt;br /&gt;&lt;br /&gt;The algorithms above can be adapted for the task of computing generalized harmonic numbers, i.e. numbers of the form 1 + 1/2&lt;sup&gt;&lt;i&gt;k&lt;/i&gt;&lt;/sup&gt; + 1/3&lt;sup&gt;&lt;i&gt;k&lt;/i&gt;&lt;/sup&gt; + ... + 1/&lt;i&gt;n&lt;/i&gt;&lt;sup&gt;&lt;i&gt;k&lt;/i&gt;&lt;/sup&gt; for some integer &lt;i&gt;k&lt;/i&gt;.  Also worth noting is that algorithm 5 allows for computation of &lt;a href="http://en.wikipedia.org/wiki/Stirling_numbers_of_the_first_kind"&gt;Stirling numbers of the first kind&lt;/a&gt;. Since |S(&lt;i&gt;n&lt;/i&gt;+1,2)| = &lt;i&gt;n&lt;/i&gt;! &amp;middot; &lt;i&gt;H&lt;sub&gt;n&lt;/sub&gt;&lt;/i&gt;, algorithm 5 can be viewed as an efficient algorithm for simultaneous computation of S(&lt;i&gt;n&lt;/i&gt;,2) and &lt;i&gt;n&lt;/i&gt;!. Expressing Stirling numbers in terms of generalized harmonic numbers, this extends to an algorithm for S(&lt;i&gt;n&lt;/i&gt;,&lt;i&gt;k&lt;/i&gt;) for any (large) &lt;i&gt;n&lt;/i&gt; and (small) &lt;i&gt;k&lt;/i&gt;.&lt;br /&gt;&lt;br /&gt;A final remark: binary splitting is not the fastest algorithm for computing factorials. The fastest known method is to decompose &lt;i&gt;n&lt;/i&gt;! into a product of prime powers and perform balanced multiplication-exponentiation. Can this idea be applied to harmonic numbers and/or Stirling numbers? It's not obvious to me how it would be done. Are there any other, possibly even faster algorithms for harmonic numbers? I've looked around, but been unable to find anything on the subject.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4754734402679928849-2577870337778486365?l=fredrik-j.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://fredrik-j.blogspot.com/feeds/2577870337778486365/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=4754734402679928849&amp;postID=2577870337778486365' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4754734402679928849/posts/default/2577870337778486365'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4754734402679928849/posts/default/2577870337778486365'/><link rel='alternate' type='text/html' href='http://fredrik-j.blogspot.com/2009/02/how-not-to-compute-harmonic-numbers.html' title='How (not) to compute harmonic numbers'/><author><name>Fredrik Johansson</name><uri>http://www.blogger.com/profile/01465400860530971858</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4754734402679928849.post-2415432367831727513</id><published>2009-02-19T08:52:00.004+01:00</published><updated>2009-02-19T10:46:46.514+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='sympy'/><category scheme='http://www.blogger.com/atom/ns#' term='mpmath'/><category scheme='http://www.blogger.com/atom/ns#' term='python'/><title type='text'>Python 3.1 twice as fast as 3.0</title><content type='html'>For large-integer arithmetic, that is. Mark Dickinson has beeen working on &lt;a href="http://bugs.python.org/issue4258"&gt;a patch&lt;/a&gt; that changes the internal representation of long integers from 15-bit to 30-bit digits, and there is an additional patch that adds a few algorithmic optimizations, written by Mark and Mario Pernici. Likely both patches will pass review and be included in Python 3.1. I asked Mark to run the mpmath unit tests to find out how big a difference the patches make, which he kindly did. Here are the results:&lt;pre&gt;&lt;br /&gt;    32-bit build:&lt;br /&gt;    unpatched (15-bit digits): 42.91 seconds&lt;br /&gt;    patched (30-bit digits): 40.57 seconds&lt;br /&gt;    patched+optimized: 30.15 seconds&lt;br /&gt;&lt;br /&gt;    64-bit build:&lt;br /&gt;    unpatched: 38.39 seconds&lt;br /&gt;    patched: 22.36 seconds&lt;br /&gt;    patched+optimized: 21.55 seconds&lt;br /&gt;    patched+optimized+bitlength: 20.20 seconds&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;The last result includes the use of the new &lt;a href="http://docs.python.org/dev/py3k/whatsnew/3.1.html"&gt;int.bit_length()&lt;/a&gt; method (which I had a small part in implementing) instead of the pure-Python version in mpmath.&lt;br /&gt;&lt;br /&gt;It's not surprising that the patches make high-precision arithmetic much faster, but most of the mpmath tests actually work at low precision and depend mostly on general speed of the Python interpreter. There the speedup is perhaps of order 0-30%. For the tests working at very high precision, the improvement is a factor 4 or 5 with the 64-bit build. Then there are a number of tests in between. With some imagination, all unit tests together provide a plausible estimate of actual performance for a wide range of applications.&lt;br /&gt;&lt;br /&gt;An excerpt of before/after results for some particular tests, comparing 64-bit unpatched and 64-bit patched+optimized+bitlength:&lt;pre&gt;&lt;br /&gt;    # Only tests 53-bit precision&lt;br /&gt;    double_compatibility      ok        1.3149240 s&lt;br /&gt;    double_compatibility      ok        1.1979949 s&lt;br /&gt;&lt;br /&gt;    # Logarithms up to 10,000 digits&lt;br /&gt;    log_hp                    ok        4.0845711 s&lt;br /&gt;    log_hp                    ok        0.7967579 s&lt;br /&gt;&lt;br /&gt;    # Large Bernoulli numbers&lt;br /&gt;    bernoulli                 ok        6.8625491 s&lt;br /&gt;    bernoulli                 ok        1.4261260 s&lt;br /&gt;&lt;br /&gt;    # Gamma function, high precision&lt;br /&gt;    gamma_huge_2              ok        2.4907949 s&lt;br /&gt;    gamma_huge_2              ok        0.5781031 s&lt;br /&gt;&lt;br /&gt;    # Numerical quadrature, 15 to 100 digit precision&lt;br /&gt;    basic_integrals           ok        3.0117619 s&lt;br /&gt;    basic_integrals           ok        1.8687689 s&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Mpmath does not yet run on Python 3.x; the benchmarking was made with a version of mpmath hacked slightly for the tests to pass. It would be nice to provide a 3.x compatible version of mpmath soon. The &lt;a href="http://docs.python.org/library/2to3.html"&gt;2to3&lt;/a&gt; tool fortunately handles almost all necessary patching; a handful of fairly simple additional changes need to be made. The most annoying problem is the lack of &lt;tt&gt;cmp&lt;/tt&gt; (and particularly the &lt;tt&gt;cmp&lt;/tt&gt; argument for &lt;tt&gt;sorted&lt;/tt&gt;), which has no trivial workaround, but still should not be too hard to fix. In any case, it seems likely that the 30-bit patch will also be backported to Python 2.7, so most users should be able to take advantage of it.&lt;br /&gt;&lt;br /&gt;It would be interesting to compare these benchmarks with the &lt;a href="http://code.google.com/p/gmpy/"&gt;GMPY&lt;/a&gt; mode in mpmath. GMPY too provides a ~2x speedup for the unit tests. (This factor would be much larger if tests at even higher precision were included. At a million digits, GMPY is orders of magnitude faster than pure Python.) Originally GMPY only sped up the very high precision tests, and was otherwise slower than pure Python, probably due to Python's internal optimizations for &lt;tt&gt;int&lt;/tt&gt; and &lt;tt&gt;long&lt;/tt&gt; instances. This disadvantage was eliminated by implementing some helper functions for mpmath in C in GMPY. Mario Pernici has recently worked on &lt;a href="http://code.google.com/p/mpmath/issues/detail?id=94"&gt;further optimizations&lt;/a&gt; along the same lines, which should substantially improve low-precision performance.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4754734402679928849-2415432367831727513?l=fredrik-j.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://fredrik-j.blogspot.com/feeds/2415432367831727513/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=4754734402679928849&amp;postID=2415432367831727513' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4754734402679928849/posts/default/2415432367831727513'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4754734402679928849/posts/default/2415432367831727513'/><link rel='alternate' type='text/html' href='http://fredrik-j.blogspot.com/2009/02/python-31-twice-as-fast-as-30.html' title='Python 3.1 twice as fast as 3.0'/><author><name>Fredrik Johansson</name><uri>http://www.blogger.com/profile/01465400860530971858</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4754734402679928849.post-2333735296892392601</id><published>2009-02-17T15:38:00.008+01:00</published><updated>2009-02-17T18:37:33.441+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='sympy'/><category scheme='http://www.blogger.com/atom/ns#' term='mpmath'/><category scheme='http://www.blogger.com/atom/ns#' term='math'/><title type='text'>Approximate prime counting</title><content type='html'>I'm continuing my quest for the &lt;a href="http://mpmath.googlecode.com/svn/trunk/doc/build/functions/index.html"&gt;special functions coverage in mpmath&lt;/a&gt; to match the &lt;a href="http://reference.wolfram.com/mathematica/guide/MathematicalFunctions.html"&gt;coverage in Mathematica&lt;/a&gt;. Comparisons are not quite fair, because mpmath is (with a few exceptions) numerical-only, and it is missing some heavy-duty functions such as the &lt;a href="http://en.wikipedia.org/wiki/Meijer_G-function"&gt;Meijer G-function&lt;/a&gt; (and will for the foreseeable future). Many functions in mpmath are also implemented on smaller domains and less rigorously. On the other hand, Mathematica is more restricted in that it only supports machine-precision exponents. And most importantly, the Mathematica source code is very difficult to read for most people.&lt;br /&gt;&lt;br /&gt;In any case, I'm trying to catch up with Mathematica 7, which &lt;a href="http://www.wolfram.com/products/mathematica/newin7/content/NewCategoriesOfSpecialFunctions/"&gt;added a whole slew&lt;/a&gt; of functions (not that I've caught up with Mathematica 6 or even 5). I did add a few functions in &lt;a href="http://fredrik-j.blogspot.com/2009/01/mpmath-011-released.html"&gt;version 0.11&lt;/a&gt; of mpmath as a consequence of seeing them in Mathematica 7 (as I recall right now, the Barnes G-function, hyperfactorial and generalized Stieltjes constants). See also the "&lt;a href="http://fredrik-j.blogspot.com/2009/02/fun-with-zeta-functions.html"&gt;Fun with zeta functions&lt;/a&gt;" post, which discussed the addition of the Riemann-Siegel functions in mpmath.&lt;br /&gt;&lt;br /&gt;I just committed an implementation of the &lt;a href="http://mathworld.wolfram.com/RiemannPrimeCountingFunction.html"&gt;Riemann R function&lt;/a&gt; R(&lt;i&gt;x&lt;/i&gt;) (also discovered in the "new in Mathematica 7" list), which is an analytic function that closely approximates the prime counting function &amp;pi;(&lt;i&gt;x&lt;/i&gt;). The incredible accuracy of the Riemann R function approximation can be visualized by plotting it against the exact prime counting funtion (I added a naive implementation of &amp;pi;(&lt;i&gt;x&lt;/i&gt;) as &lt;tt&gt;primepi&lt;/tt&gt;, mainly to facilitate this kind of comparison -- &lt;a href="http://code.google.com/p/sympy/"&gt;SymPy&lt;/a&gt; contains a slightly more optimized &lt;tt&gt;primepi&lt;/tt&gt; which could be used instead):&lt;pre&gt;&lt;br /&gt;&gt;&gt;&gt; from mpmath import *&lt;br /&gt;&gt;&gt;&gt; plot([primepi, riemannr], [0,100])&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_rh0QblLk0C0/SZrQpI9vXDI/AAAAAAAAAD0/ErgqDXBEv7A/s1600-h/riemannr.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 400px; height: 302px;" src="http://4.bp.blogspot.co
