def sign(x): """Return sign(x), defined as x/abs(x), or 0 for x = 0.""" x = convert_lossless(x) if not x or isnan(x): return x if isinstance(x, mpf): return cmp(x, 0) return x / abs(x)
def default_color_function(z): if isinf(z): return (1.0, 1.0, 1.0) if isnan(z): return (0.5, 0.5, 0.5) pi = 3.1415926535898 a = (float(arg(z)) + pi) / (2 * pi) a = (a + 0.5) % 1.0 b = 1.0 - float(1 / (1.0 + abs(z)**0.3)) return hls_to_rgb(a, b, 0.8)
def default_color_function(z): if isinf(z): return (1.0, 1.0, 1.0) if isnan(z): return (0.5, 0.5, 0.5) pi = 3.1415926535898 a = (float(arg(z)) + pi) / (2*pi) a = (a + 0.5) % 1.0 b = 1.0 - float(1/(1.0+abs(z)**0.3)) return hls_to_rgb(a, b, 0.8)
def ellipk(m): """Complete elliptic integral of the first kind, K(m). Note that the argument is the parameter m = k^2, not the modulus k.""" # Poor implementation: # return pi/2 * sum_hyp2f1_rat((1,2),(1,2),(1,1), m) if m == 1: return inf if isnan(m): return m if isinf(m): return 1/m s = sqrt(m) a = (1-s)/(1+s) v = pi/4*(1+a)/agm(1,a) if isinstance(m, mpf) and m < 1: return v.real return v
def ellipk(m): """Complete elliptic integral of the first kind, K(m). Note that the argument is the parameter m = k^2, not the modulus k.""" # Poor implementation: # return pi/2 * sum_hyp2f1_rat((1,2),(1,2),(1,1), m) if m == 1: return inf if isnan(m): return m if isinf(m): return 1 / m s = sqrt(m) a = (1 - s) / (1 + s) v = pi / 4 * (1 + a) / agm(1, a) if isinstance(m, mpf) and m < 1: return v.real return v
def plot(f, xlim=[-5, 5], ylim=None, points=200, file=None, dpi=None, singularities=[]): """ Shows a simple 2D plot of a function or list of functions over a given interval. Some examples: plot(lambda x: exp(x)*li(x), [1, 4]) plot([cos, sin], [-4, 4]) plot([fresnels, fresnelc], [-4, 4]) plot([sqrt, cbrt], [-4, 4]) plot(lambda t: zeta(0.5+t*j), [-20, 20]) plot([floor, ceil, abs, sign], [-5, 5]) Points where the function raises a numerical exception or returns an infinite value are removed from the graph. For parts where the function assumes complex values, the real part is plotted with dashes and the imaginary part is plotted with dots. NOTE: This function requires matplotlib (pylab). """ import pylab pylab.clf() if not isinstance(f, (tuple, list)): f = [f] a, b = xlim colors = ['b', 'r', 'g', 'm', 'k'] for n, func in enumerate(f): x = arange(a, b, (b - a) / float(points)) segments = [] segment = [] in_complex = False for i in xrange(len(x)): try: if i != 0: for sing in singularities: if x[i - 1] <= sing and x[i] >= sing: raise ValueError v = func(x[i]) if isnan(v) or abs(v) == inf: raise ValueError if isinstance(v, complex_types): re = float(v.real) im = float(v.imag) if not in_complex: in_complex = True segments.append(segment) segment = [] segment.append((float(x[i]), re, im)) else: if in_complex: in_complex = False segments.append(segment) segment = [] segment.append((float(x[i]), v)) except plot_ignore: if segment: segments.append(segment) segment = [] if segment: segments.append(segment) for segment in segments: x = [s[0] for s in segment] y = [s[1] for s in segment] if not x: continue c = colors[n % len(colors)] if len(segment[0]) == 3: z = [s[2] for s in segment] pylab.plot(x, y, '--' + c, linewidth=3) pylab.plot(x, z, ':' + c, linewidth=3) else: pylab.plot(x, y, c, linewidth=3) pylab.xlim(xlim) if ylim: pylab.ylim(ylim) pylab.xlabel('x') pylab.ylabel('f(x)') pylab.grid(True) if file: pylab.savefig(file, dpi=dpi) else: pylab.show()
def lambertw(z, k=0, approx=None): """ lambertw(z,k) gives the kth branch of the Lambert W function W(z), defined as the kth solution of z = W(z)*exp(W(z)). lambertw(z) == lambertw(z, k=0) gives the principal branch value (0th branch solution), which is real for z > -1/e . The k = -1 branch is real for -1/e < z < 0. All branches except k = 0 have a logarithmic singularity at 0. The definition, implementation and choice of branches is based on Corless et al, "On the Lambert W function", Adv. Comp. Math. 5 (1996) 329-359, available online here: http://www.apmaths.uwo.ca/~djeffrey/Offprints/W-adv-cm.pdf TODO: use a series expansion when extremely close to the branch point at -1/e and make sure that the proper branch is chosen there """ if isnan(z): return z mp.prec += 20 # We must be extremely careful near the singularities at -1/e and 0 u = exp(-1) if abs(z) <= u: if not z: # w(0,0) = 0; for all other branches we hit the pole if not k: return z return -inf if not k: w = z # For small real z < 0, the -1 branch behaves roughly like log(-z) elif k == -1 and not z.imag and z.real < 0: w = log(-z) # Use a simple asymptotic approximation. else: w = log(z) # The branches are roughly logarithmic. This approximation # gets better for large |k|; need to check that this always # works for k ~= -1, 0, 1. if k: w += k * 2*pi*j elif k == 0 and z.imag and abs(z) <= 0.6: w = z else: if z == inf: return z if z == -inf: return nan # Simple asymptotic approximation as above w = log(z) if k: w += k * 2*pi*j # Use Halley iteration to solve w*exp(w) = z two = mpf(2) weps = ldexp(eps, 15) for i in xrange(100): ew = exp(w) wew = w*ew wewz = wew-z wn = w - wewz/(wew+ew-(w+two)*wewz/(two*w+two)) if abs(wn-w) < weps*abs(wn): return wn else: w = wn print "Warning: Lambert W iteration failed to converge:", z return wn
def plot(f, xlim=[-5,5], ylim=None, points=200, file=None, dpi=None, singularities=[]): r""" Shows a simple 2D plot of a function `f(x)` or list of functions `[f_0(x), f_1(x), \ldots, f_n(x)]` over a given interval specified by *xlim*. Some examples:: plot(lambda x: exp(x)*li(x), [1, 4]) plot([cos, sin], [-4, 4]) plot([fresnels, fresnelc], [-4, 4]) plot([sqrt, cbrt], [-4, 4]) plot(lambda t: zeta(0.5+t*j), [-20, 20]) plot([floor, ceil, abs, sign], [-5, 5]) Points where the function raises a numerical exception or returns an infinite value are removed from the graph. Singularities can also be excluded explicitly as follows (useful for removing erroneous vertical lines):: plot(cot, ylim=[-5, 5]) # bad plot(cot, ylim=[-5, 5], singularities=[-pi, 0, pi]) # good For parts where the function assumes complex values, the real part is plotted with dashes and the imaginary part is plotted with dots. NOTE: This function requires matplotlib (pylab). """ import pylab pylab.clf() if not isinstance(f, (tuple, list)): f = [f] a, b = xlim colors = ['b', 'r', 'g', 'm', 'k'] for n, func in enumerate(f): x = arange(a, b, (b-a)/float(points)) segments = [] segment = [] in_complex = False for i in xrange(len(x)): try: if i != 0: for sing in singularities: if x[i-1] <= sing and x[i] >= sing: raise ValueError v = func(x[i]) if isnan(v) or abs(v) > 1e300: raise ValueError if isinstance(v, complex_types): re = float(v.real) im = float(v.imag) if not in_complex: in_complex = True segments.append(segment) segment = [] segment.append((float(x[i]), re, im)) else: if in_complex: in_complex = False segments.append(segment) segment = [] segment.append((float(x[i]), v)) except plot_ignore: if segment: segments.append(segment) segment = [] if segment: segments.append(segment) for segment in segments: x = [s[0] for s in segment] y = [s[1] for s in segment] if not x: continue c = colors[n % len(colors)] if len(segment[0]) == 3: z = [s[2] for s in segment] pylab.plot(x, y, '--'+c, linewidth=3) pylab.plot(x, z, ':'+c, linewidth=3) else: pylab.plot(x, y, c, linewidth=3) pylab.xlim(xlim) if ylim: pylab.ylim(ylim) pylab.xlabel('x') pylab.ylabel('f(x)') pylab.grid(True) if file: pylab.savefig(file, dpi=dpi) else: pylab.show()
def lambertw(z, k=0, approx=None): """ lambertw(z,k) gives the kth branch of the Lambert W function W(z), defined as the kth solution of z = W(z)*exp(W(z)). lambertw(z) == lambertw(z, k=0) gives the principal branch value (0th branch solution), which is real for z > -1/e . The k = -1 branch is real for -1/e < z < 0. All branches except k = 0 have a logarithmic singularity at 0. The definition, implementation and choice of branches is based on Corless et al, "On the Lambert W function", Adv. Comp. Math. 5 (1996) 329-359, available online here: http://www.apmaths.uwo.ca/~djeffrey/Offprints/W-adv-cm.pdf TODO: use a series expansion when extremely close to the branch point at -1/e and make sure that the proper branch is chosen there """ if isnan(z): return z mp.prec += 20 # We must be extremely careful near the singularities at -1/e and 0 u = exp(-1) if abs(z) <= u: if not z: # w(0,0) = 0; for all other branches we hit the pole if not k: return z return -inf if not k: w = z # For small real z < 0, the -1 branch behaves roughly like log(-z) elif k == -1 and not z.imag and z.real < 0: w = log(-z) # Use a simple asymptotic approximation. else: w = log(z) # The branches are roughly logarithmic. This approximation # gets better for large |k|; need to check that this always # works for k ~= -1, 0, 1. if k: w += k * 2 * pi * j elif k == 0 and z.imag and abs(z) <= 0.6: w = z else: if z == inf: return z if z == -inf: return nan # Simple asymptotic approximation as above w = log(z) if k: w += k * 2 * pi * j # Use Halley iteration to solve w*exp(w) = z two = mpf(2) weps = ldexp(eps, 15) for i in xrange(100): ew = exp(w) wew = w * ew wewz = wew - z wn = w - wewz / (wew + ew - (w + two) * wewz / (two * w + two)) if abs(wn - w) < weps * abs(wn): return wn else: w = wn print "Warning: Lambert W iteration failed to converge:", z return wn