In [62]:
from mpmath import mpf, mp
import pickle
import sys
%pylab inline --no-import-all
from pylab import *
# Constants for bits of significand precision
# TODO these should be for mp.prec, not mp.dps
SINGLE = 24
DOUBLE = 53
def vectorize(functions):
"""
Takes a list of functions and returns a function that accepts a list of
values and applies each function given to the list of values.
"""
def _vectorized(x, ys):
return tuple(f(x, *ys) for f in functions)
return _vectorized
def inc_vec(vec, inc):
return [x + inc for x in vec]
def add_vec(*vecs):
return [sum(nums) for nums in zip(*vecs)]
def mul_vec(vec, mul):
return [x * mul for x in vec]
def rk4_system(y, x0, y0, h, steps):
"""
y: list of ODE
y0: vector (tuple) of initial y values
Finds y(x0 + h * steps)
"""
x1 = x0
y1 = list(y0)
xs = [x0]
ys = [[y0[i]] for i,f in enumerate(y)]
f = vectorize(y)
for i in range(steps):
k1 = f(x1, y1)
k2 = f(x1 + (h / 2), add_vec(y1, mul_vec(k1, h / 2)))
k3 = f(x1 + (h / 2), add_vec(y1, mul_vec(k2, h / 2)))
k4 = f(x1 + h, add_vec(y1, mul_vec(k3, h)))
y1 = add_vec(y1, mul_vec(add_vec(k1, mul_vec(k2, 2), mul_vec(k3, 2), k4), h / 6))
for i, val in enumerate(y1):
ys[i].append(val)
x1 += h
xs.append(x1)
return xs, ys
def runrange(file_suffix, a15rate, precision, steps):
IS_MPMATH = isinstance(precision, int)
print("Using mpmath:", IS_MPMATH)
if IS_MPMATH:
a1 = mpf('2')
a2 = mpf('2')
a3 = mpf('3')
a4 = mpf('0.7')
a5 = mpf('1')
a6 = mpf('0.02')
a7 = mpf('20')
a8 = mpf('1')
a9 = mpf('1')
a10 = mpf('1')
a11 = mpf('0.01')
a12 = mpf('0.1')
a13 = mpf('0.3')
a14 = mpf('50')
a16 = mpf('0.1')
a17 = mpf('0.14')
B = mpf('1')
start = mpf('0')
initialconds = (mpf('0.5'), mpf('0'), mpf('1'), mpf('0.1'))
delta = mpf('0.1')
mp.dps = precision
print("Precision:", mp.dps)
else:
a1 = 2.0
a2 = 2.0
a3 = 3.0
a4 = 0.7
a5 = 1.0
a6 = 0.02
a7 = 20.0
a8 = 1.0
a9 = 1.0
a10 = 1.0
a11 = 0.01
a12 = 0.1
a13 = 0.3
a14 = 50.0
a16 = 0.1
a17 = 0.14
B = 1
start = 0.0
initialconds = (0.5, 0.0, 1.0, 0.1)
delta = 0.1
print("Precision: Python float (double)", sys.float_info)
f1 = lambda p: p**a1 / (a2**a1 + p**a1)
f2 = lambda p: a4 * a5**a3 / (a5**a3 + p**a3)
p = lambda E, a15: (a14 * E * B) / a15
y = [
lambda t, A, M, E, a15: f1(p(E,a15))*(a6+a7*M)-a8*A-a9*A**2, #dA/dt
lambda t, A, M, E, a15: a10*f2(p(E,a15))*A-f1(p(E,a15))*a16*a7*M-a11*M, #dM/dt
lambda t, A, M, E, a15: a12*(1-f2(p(E,a15)))*A-a13*E, #dE/dt,
lambda t, A, M, E, a15: a15rate
]
xs, ys = rk4_system(y, start, initialconds, delta, steps)
pickle.dump(list(zip(xs, *ys)), open('time_py_np_{}.dat'.format(file_suffix), 'wb'))
In [42]:
runrange('slow_single', 0.0002, float, 150000)
#runrange('fast_single', mpf('0.01'), SINGLE)
In [46]:
runrange('slow_100', mpf('0.0002'), 100, 150000)
#runrange('fast_double', mpf('0.01'), DOUBLE)
In [47]:
ssingle = pickle.load(open('time_py_np_slow_single.dat', 'rb'))
sdouble = pickle.load(open('time_py_np_slow_100.dat', 'rb'))
In [93]:
matplotlib.rcParams.update({'font.size': 14})
matplotlib.rcParams.update({'figure.autolayout': True})
In [94]:
figure()
title("Python float (double precision; 53 bits)")
plot([float(x[4]) for x in ssingle], [float(x[1]) for x in ssingle])
xlabel("delta_p")
ylabel("A")
axvline(0.5707, color='black')
axhline(color='black')
annotate("Hopf bifurcation", (0.5707, 2), xytext=(0.8,2.5), arrowprops=dict(facecolor='black',lw=0.01))
savefig("precision_double.png")
In [97]:
figure()
title("mpmath float (336 bits precision)")
ylim(0,3)
plot([float(x[4]) for x in sdouble], [float(x[1]) for x in sdouble])
xlabel("delta_p")
ylabel("A")
axvline(0.5707, color='black')
axhline(color='black')
annotate("Hopf bifurcation", (0.5707, 2), xytext=(0.8,2.5), arrowprops=dict(facecolor='black',lw=0.01))
savefig("precision_100.png")
In [ ]: