TACC -- 3. Tutorial on Numerical Computation in Sage
William Stein
University of Washington
This tutorial is mainly about using Sage for numerical computation (more like MATLAB). Sage comes with several numerical libraries:
{{{id=5| /// }}}
Numerical Root Finding: Bisections, Newton's Method, Polynomials
Sage has a really nice fast float function that given a symbolic expression returns a very fast callable object. This is really useful to know about when implementing root finding or if you call any functions from scipy. At present, _fast_float_ can easily be 1000 times faster than not using it! (Which means we probably need to start automatically implicitly using it!) Anyway, here are some examples.
{{{id=34| var('x') f = cos(x)^2 - x^3 + x - 5 parent(f) /// Symbolic Ring }}} {{{id=36| a = float(e); a /// 2.7182818284590451 }}} {{{id=37| f(x=a) /// -21.5359963633559 }}} {{{id=89| type(f(x=a)) ///It works with n variables as well.
{{{id=35| f(x,y) = x^3 + y^2 - cos(x-y)^3 /// }}} {{{id=90| a = float(e) /// }}} {{{id=39| timeit('f(a,a)') /// 625 loops, best of 3: 253 µs per loop }}} {{{id=40| g = fast_float(f, x,y) /// }}} {{{id=41| timeit('g(a,a)') /// 625 loops, best of 3: 443 ns per loop }}} {{{id=2| /// }}} {{{id=43| /// }}} {{{id=98| /// }}}Suppose $f(x)$ is a continuous real-valued function on an interval $[a,b]$ and that $f(a)f(b) < 0$, i.e., there is a sign change. Then by calculus $f$ has a zero on this interval.
{{{id=3| f = cos(x)-x show(plot(f, 0.1,1.5),xmin=0) /// }}}The main goal is to understand various ways of numerically finding a point $c \in [a,b]$ such that $f(c)$ is very close to 0.
Perhaps the simplest method is bisection. Given $\epsilon>0$, the following algorithm find a value $c\in [a,b]$ such that $|f(c)| < \epsilon$.
At every step in the algorithm there is a point $c$ in the interval so that $f(c) = 0$. Since the width of the interval halves each time and each interval contains a zero of $f$, the sequence of intervals will converge to some root of $f$. Since $f$ is continuous, after a finite number of steps we will find a $c$ such that $|f(c)| < \epsilon$.
{{{id=11| def bisect_method(f, a, b, eps): try: f = fast_float(f, f.variables()[0]) except AttributeError: print "WARNING: Not using fast_float." intervals = [(a,b)] two = float(2); eps = float(eps) while True: c = (a+b)/two fa = f(a); fb = f(b); fc = f(c) if abs(fc) < eps: return c, intervals if fa*fc < 0: a, b = a, c elif fc*fb < 0: a, b = c, b else: raise ValueError, "f must have a sign change in the interval (%s,%s)"%(a,b) intervals.append((a,b)) html("The bisect we implemented above is slower:
{{{id=61| bisect_method(f, a, b, 1e-8)[0] /// 0.73908513784408569 }}} {{{id=102| timeit('bisect_method(g, a, b, 1e-8)[0]') /// 625 loops, best of 3: 446 µs per loop }}} {{{id=30| /// }}}Scipy's bisect is a lot faster than ours, of course. It's optimized code written in C. Let's look.
wstein> ls spkg/standard/scipy*
spkg/standard/scipy-0.7.p4.spkg
spkg/standard/scipy_sandbox-20071020.p4.spkg
wstein> tar jxvf spkg/standard/scipy-0.7.p4.spkg # may have to get from http://sagemath.org/packages/standard/
scipy-0.7.p4/src/scipy/optimize/Zeros/bisect.cHere it is:
/* Written by Charles Harris charles.harris@sdl.usu.edu */ #include "zeros.h" double bisect(callback_type f, double xa, double xb, double xtol, double rtol, int iter, default_parameters *params) { int i; double dm,xm,fm,fa,fb,tol; tol = xtol + rtol*(fabs(xa) + fabs(xb)); fa = (*f)(xa,params); fb = (*f)(xb,params); params->funcalls = 2; if (fa*fb > 0) {ERROR(params,SIGNERR,0.0);} if (fa == 0) return xa; if (fb == 0) return xb; dm = xb - xa; params->iterations = 0; for(i=0; iiterations++; dm *= .5; xm = xa + dm; fm = (*f)(xm,params); params->funcalls++; if (fm*fa >= 0) { xa = xm; } if (fm == 0 || fabs(dm) < tol) return xm; } ERROR(params,CONVERR,xa); }
We can implement this directly in Sage using Cython (see http://cython.org -- in fact, browse that page for a while!).
Problem: Click on each of the .html link above to see the autogenerated code (double click on a line of Cython to see the corresponding C code).
{{{id=105| /// }}} {{{id=64| f = cos(x) - x g = fast_float(f, x) print bisect_cython(g, 0.0, 1.0, 1e-16) print scipy.optimize.bisect(g, 0.0, 1.0, maxiter=40) /// (0.73908513321516067, 52) 0.739085133216 }}} {{{id=106| /// }}}Note: Putting r after a number keeps the Sage preparser from turning it into a Sage arbitrary precision real number (instead of a standard Python float).
{{{id=59| print "scipy" timeit('scipy.optimize.bisect(g, 0.0r, 1.0r, maxiter=40)') print "sage/python" timeit('bisect_method(g, 0.0r, 1.0r, 1e-16r)') print "sage/cython" timeit('bisect_cython(g, 0.0r, 1.0r, 1e-16r)') /// scipy 625 loops, best of 3: 17.5 µs per loop sage/python 625 loops, best of 3: 883 µs per loop sage/cython 625 loops, best of 3: 15.1 µs per loop }}} {{{id=63| /// }}} {{{id=68| /// }}} {{{id=65| /// }}} {{{id=10| /// }}}Write our own Newton method in Cython:
{{{id=23| %cython def newton_cython(f, double c, fprime, double eps, int maxiter=100): cdef double fc cdef int i for i from 0 <= i < maxiter: fc = f(c) absfc = -fc if fc < 0 else fc if absfc < eps: return c c = c - fc/fprime(c) return c /// }}}Test them all
{{{id=80| x = var('x') f = (x^2 - cos(x) + sin(x^2))^3 g = f._fast_float_(x) gprime = f.derivative()._fast_float_(x) timeit('scipy.optimize.newton(g, 0.5r, gprime)') timeit('newton_cython(g, 0.5r, gprime, 1e-13)') /// 625 loops, best of 3: 127 µs per loop 625 loops, best of 3: 37.9 µs per loop }}} {{{id=82| newton_cython(g, 0.5r, gprime, 1e-13) /// 0.63810381041535857 }}} {{{id=6| plot(g, 0,1) ///Sage has code that uses Scipy behind the scenes. With your help it could have a lot more!
{{{id=86| var('x') f = cos(x)-x /// }}} {{{id=85| f.find_root(0,1) /// 0.7390851332151559 }}} {{{id=109| /// }}}Problem: Find a root of something else on another interval.
{{{id=83| /// }}}Problem: Type f.find_root?? to read the source code. The function just calls into another module. Type sage.numerical.optimize.find_root?? to read the source code of that, which does involve calling scipy.
{{{id=114| /// }}} {{{id=16| /// }}} {{{id=115| /// }}}Sage is also a computer algebra system, so it also has subtle algorithms such as real root isolation.
{{{id=75| R.