I wrote this program describing an algorithm for the simulation of a partial differential equation. The basic functions I use are defined by
import numpy as np
import math
from scipy import integrate, stats
def shift(func, x, a=0):
return func(x-a)
def scale(func, a=1):
return a*func
def trunc(func, x):
if x <= 0:
return 0
else:
return func(x)
def quad(func, a, b):
return integrate.quad(func, a, b)
def gauss(func, t, x):
def pregau(z):
k = (-t ** (1 / 2)) * z
return shift(func, x, k)*math.exp(-(z**2)/2)
fa = (1 / ((2 * math.pi) ** (1 / 2)) * integrate.quad(pregau, -np.inf, np.inf)[0])
return fa
The program then simulates the solution to the partial differential equation by
def vundl(x, u, l0=0.0, a=a, b=b, c=c):
v = [u(x)]
l = [l0]
f_temp_rec = u
for i in range(10):
def f_temp(x):
y = x - c * dt + B[i + 1] * 2 * a
z = b * dt
return gauss(f_temp_rec, z, y)
li = l[i] + quad(f_temp, 0, np.inf)[0]
l = np.append(l, li)
if x <= 0:
v = np.append(v, 0)
f_temp_rec = 0
else:
f_temp_rec = f_temp
v = np.append(v, f_temp(x))
return [v, l]
def u0(x):
return stats.beta.pdf(x, 2.7, 3.05)
print(vundl(x = 0.5, u0))
If I run this program for N=0 it produces a vector. Running the program for N>0 gives me the following error:
"RecursionError: maximum recursion depth exceeded"
but it actually should give me a vector v and a vector l.
I'm not sure exactly what you're trying to do in vundl with f_temp and f_temp_rec, but in the else: block you assign:
f_temp_rec = f_temp
and then call f_temp which calls gauss(f_temp_rec, z, y). Since at this point f_temp_rec is f_temp the function f_temp calls itself in an infinite recursion.
You should be able to see in in traceback that f_temp is calling itself repeatedly.
Related
In the code below, inside WKB, I'm trying to pass the function "integrand" as an argument of the function int_gauss (like this: int_gauss(integrand(E,x0,m1,x),xp,wp)) and it returns me an error.
TypeError: 'numpy.ndarray' object is not callable
Thanks in advance for your help.
x0=1
m1=1
h=1
e=100
import numpy as np
import matplotlib.pyplot as plt
def gaussxw(N):
a = np.linspace(3,4*N-1,N)/(4*N+2)
x = np.cos(np.pi*a+1/(8*N*N*np.tan(a)))
# Find roots using Newton's method
epsilon = 1e-15
delta = 1.0
while delta>epsilon:
p0 = np.ones(N,float)
p1 = np.copy(x)
for k in range(1,N):
p0,p1 = p1,((2*k+1)*x*p1-k*p0)/(k+1)
dp = (N+1)*(p0-x*p1)/(1-x*x)
dx = p1/dp
x -= dx
delta = max(abs(dx))
# Calculate the weights
w = 2*(N+1)*(N+1)/(N*N*(1-x*x)*dp*dp)
return x,w
def gaussxwab(N,a,b):
x,w = gaussxw(N)
return 0.5*(b-a)*x+0.5*(b+a),0.5*(b-a)*w
N = 50
x, w = gaussxw(N)
def integrand(E,x0,m1,x):
return np.sqrt((2*m1*(E- (e*( ((x0/x)**12) -2*((x0/x)**6) ) ))))
def WKB(E,x0,e):
a, b = x0*(1+np.sqrt(1+(E/e)))**(-1/6), x0*(1-np.sqrt(1+(E/e)))**(-1/6)
xp = 0.5*(b - a)*x + 0.5*(b + a)
wp = 0.5*(b - a)*w
return int_gauss(integrand(E,x0,m1,x),xp,wp)
def int_gauss(f,nodes, weights):
result = 0.0
for x, w in zip(nodes, weights):
result += w *f(x)
return result
plt.close()
En=np.linspace(-100,-1,496)
W=[WKB(i,x0,e) for i in En]
plt.figure()
plt.plot(En,W)
plt.grid()
plt.axis()
plt.show()
I find the desired result when I explicitly compute the desired function "integrand" inside int_gauss like this but I'd like to be able to call int_gauss with the integrand function as a parameter int_gauss(integrand(E,x0,m1,x),xp,wp) inside WKB:
def WKB(E,x0,e):
a, b = x0*(1+np.sqrt(1+(E/e)))**(-1/6), x0*(1-np.sqrt(1+(E/e)))**(-1/6)
xp = 0.5*(b - a)*x + 0.5*(b + a)
wp = 0.5*(b - a)*w
return int_gauss(E,x0,m1,xp,wp)
def int_gauss(E,x0,m1,nodes, weights):
result = 0.0
for x, w in zip(nodes, weights):
result += w * np.sqrt((2*m1*(E- (e*( ((x0/x)**12) -2*((x0/x)**6) ) ))))
return result
I'm trying to write a function to evaluate the probability mass function for the bivariate poisson distribution.
This is easy when all of the parameters (x, y, theta1, theta2, theta0) are scalars, but tricky to scale up without loops to allow these parameters to be vectors. I need it to scale such that, for:
theta0 being a scalar - the "correlation parameter" in the equation
theta1 and theta2 having length l
x, y both having length n
the output array would have shape (l, n, n). For example, a slice [j, :, :] from the output array would look like:
The first part (the constant, before the summation) I think i've figured out:
import numpy as np
from scipy.special import factorial
def constant(theta1, theta2, theta0, x, y):
exponential_part = np.exp(-(theta1 + theta2 + theta0)).reshape(-1, 1, 1)
x = np.tile(x, (len(x), 1)).transpose()
y = np.tile(y, (len(y), 1))
double_factorial = (np.power(np.array(theta1).reshape(-1, 1, 1), x)/factorial(x)) * \
(np.power(np.array(theta2).reshape(-1, 1, 1), y)/factorial(y))
return exponential_part * double_factorial
But I'm struggling with the summation part. How can I vectorize a summation where the limits depend on variable arrays?
I think I have this figured out, based on the approach that #w-m suggests: calculate every possible summation term which could appear, based on the maximum x or y value which appears, and use a mask to get rid of the ones you don't want. Assuming you have your x and y terms go from 0 to N, in consecutive order, this is calculating up to three times more terms than are actually required, but this is offset by getting to use vectorization.
Reference implementation
I wrote this by first writing a pure-Python reference implementation, which just implements your problem using loops. With 4 nested loops, it's not exactly fast, but it's handy to have while testing the numpy version.
import numpy as np
from scipy.special import factorial, comb
import operator as op
from functools import reduce
def choose(n, r):
# https://stackoverflow.com/a/4941932/530160
r = min(r, n-r)
numer = reduce(op.mul, range(n, n-r, -1), 1)
denom = reduce(op.mul, range(1, r+1), 1)
return numer // denom # or / in Python 2
def reference_impl_constant(s_theta1, s_theta2, s_theta0, s_x, s_y):
# Cast to float to prevent overflow
s_theta1 = float(s_theta1)
s_theta2 = float(s_theta2)
s_theta0 = float(s_theta0)
s_x = float(s_x)
s_y = float(s_y)
term1 = np.exp(-(s_theta1 + s_theta2 + s_theta0))
term2 = (s_theta1 ** s_x / factorial(s_x))
term3 = (s_theta2 ** s_y / factorial(s_y))
assert term1 >= 0
assert term2 >= 0
assert term3 >= 0
return term1 * term2 * term3
def reference_impl_constant_loop(theta1, theta2, theta0, x, y):
theta_len = theta1.shape[0]
xy_len = x.shape[0]
constant_array = np.zeros((theta_len, xy_len, xy_len))
for i in range(theta_len):
for j in range(xy_len):
for k in range(xy_len):
s_theta1 = theta1[i]
s_theta2 = theta2[i]
s_theta0 = theta0
s_x = x[j]
s_y = y[k]
constant_term = reference_impl_constant(s_theta1, s_theta2, s_theta0, s_x, s_y)
assert constant_term >= 0
constant_array[i, j, k] = constant_term
return constant_array
def reference_impl_summation(s_theta1, s_theta2, s_theta0, s_x, s_y):
sum_ = 0
for i in range(min(s_x, s_y) + 1):
sum_ += choose(s_x, i) * choose(s_y, i) * factorial(i) * ((s_theta0/s_theta1/s_theta2) ** i)
assert sum_ >= 0
return sum_
def reference_impl_summation_loop(theta1, theta2, theta0, x, y):
theta_len = theta1.shape[0]
xy_len = x.shape[0]
summation_array = np.zeros((theta_len, xy_len, xy_len))
for i in range(theta_len):
for j in range(xy_len):
for k in range(xy_len):
s_theta1 = theta1[i]
s_theta2 = theta2[i]
s_theta0 = theta0
s_x = x[j]
s_y = y[k]
summation_term = reference_impl_summation(s_theta1, s_theta2, s_theta0, s_x, s_y)
assert summation_term >= 0
summation_array[i, j, k] = summation_term
return summation_array
def reference_impl(theta1, theta2, theta0, x, y):
# all array inputs must be 1D
assert len(theta1.shape) == 1
assert len(theta2.shape) == 1
assert len(x.shape) == 1
assert len(y.shape) == 1
# theta vectors must have same length
theta_len = theta1.shape[0]
assert theta2.shape[0] == theta_len
# x and y must have same length
xy_len = x.shape[0]
assert y.shape[0] == xy_len
# theta0 is scalar
assert isinstance(theta0, (int, float))
constant_array = np.zeros((theta_len, xy_len, xy_len))
output = np.zeros((theta_len, xy_len, xy_len))
constant_array = reference_impl_constant_loop(theta1, theta2, theta0, x, y)
summation_array = reference_impl_summation_loop(theta1, theta2, theta0, x, y)
output = constant_array * summation_array
return output
Numpy implementation
I split the implementation of this across two functions.
The fast_constant() function calculates everything to the left of the summation symbol. The fast_summation() function calculates everything inside the summation symbol.
import numpy as np
from scipy.special import factorial, comb
def fast_summation(theta1, theta2, theta0, x, y):
x = np.tile(x, (len(x), 1)).transpose()
y = np.tile(y, (len(y), 1))
sum_limit = np.minimum(x, y)
max_sum_limit = np.max(sum_limit)
i = np.arange(max_sum_limit + 1).reshape(-1, 1, 1)
summation_mask = (i <= sum_limit)
theta_ratio = (theta0 / (theta1 * theta2)).reshape(-1, 1, 1, 1)
theta_to_power = np.power(theta_ratio, i)
terms = comb(x, i) * comb(y, i) * factorial(i) * theta_to_power
# mask out terms which aren't part of sum
terms *= summation_mask
# axis 0 is theta
# axis 1 is i
# axis 2 & 3 are x and y
# so sum across axis 1
terms = terms.sum(axis=1)
return terms
def fast_constant(theta1, theta2, theta0, x, y):
theta1 = theta1.astype('float64')
theta2 = theta2.astype('float64')
exponential_part = np.exp(-(theta1 + theta2 + theta0)).reshape(-1, 1, 1)
# x and y must be 1D
assert len(x.shape) == 1
assert len(y.shape) == 1
# x and y must have same shape
assert x.shape == y.shape
x_len, y_len = x.shape[0], y.shape[0]
x = x.reshape((x_len, 1))
y = y.reshape((1, y_len))
double_factorial = (np.power(np.array(theta1).reshape(-1, 1, 1), x)/factorial(x)) * \
(np.power(np.array(theta2).reshape(-1, 1, 1), y)/factorial(y))
return exponential_part * double_factorial
def fast_impl(theta1, theta2, theta0, x, y):
return fast_summation(theta1, theta2, theta0, x, y) * fast_constant(theta1, theta2, theta0, x, y)
Benchmarking
Assuming that X and Y range from 0 to 20, and that theta is centered somewhere inside that range, I get the result that the numpy version is roughly 280 times faster than the pure python reference.
Numerical stability
I'm unsure how numerically stable this is. For example, when I center theta at 100, I get a floating-point overflow. Typically, when computing an expression which has lots of choose and factorial expressions inside it, you'll use some mathematical equivalent which results in smaller intermediate sums. In this case I have so little understanding of the math that I don't know how you'd do that.
I am new to python and don't understand how to use the libraries properly. I am trying to write a program to compute the Taylor Series Approximation of a function centered at 0 at a given x and n.
def fact(n): #function to calculate n!
if n <= 0:
return 1
else:
return n * fact(n - 1)
#h= 0.00000000001
#def derivative(f,x,n): #function that calculates the derivative of a
function at a specified x
# return (f(x + h) - f(x - h)) /(2 * h)
from sympy import *
x = symbols('x')
def taylor(f,x,n):
for i in range(0,n):
t = 0
t = t + ((diff(f,x,n))/(fact(n))) * (x ** n)
return t
taylor(sin(x),1/32,1)
Here is what works for me after I fixed stuff from your code. I used sympy factorial.
from sympy import *
x = symbols('x')
def taylor(f,x,x0, n):
t = 0
for i in range(0,n):
t = t + ((diff(f,x,i).subs(x,x0))/(factorial(i))) * (x ** i)
return t
pprint(taylor(sin(x),x,Rational(1,32),4))
and the answer I get is
3 2
x ⋅cos(1/32) x ⋅sin(1/32)
- ──────────── - ──────────── + x⋅cos(1/32) + sin(1/32)
6 2
I'm trying to optimize simple integration in python which looks something like
from scipy import integrate
import numpy as np
from scipy.special import kv
import time
#Example function
def integrand(x, a, b, c):
return a * (x ** (-b)) * (np.sqrt(x ** (c) + 1) - 1)
#Real Function that I want to calculate
def Bes(xx):
return integrate.quad(lambda x: kv(5./3.,x), xx,np.inf)
def F(x,a,b,c,d,e,f):
zx = 1/((x**2.+1)*a)
feq = e*x**(f)
if (x>c):
feq *= c/x * np.exp(-(x/d)**2.)
return b*Bes(zx)*feq*x**2.
start = time.time()
array_length = 10
a = np.random.rand(array_length)+3.
b = np.random.rand(array_length)+1.
c = np.random.rand(array_length)
d = (np.random.rand(array_length)+1)*100.
e = np.random.rand(array_length)*100.
f = np.random.rand(array_length)
inte = np.array([])
for i in range(array_length):
result = integrate.quad(lambda x: F(x, a[i], b[i], c[i],d[i],e[i],f[i]),0.01,100000.)
inte = np.append(inte,result[0])
print("For array length = %i" % array_length)
print("Time = %.2f [sec]" %(time.time()-start))
But the problems that I'm facing are
a, b, c are array with length > 10^7 (same length)
integration range of x starts at 0.01 and extends to infinite
Integration at the small x (like [0.01, 1]) is very important and needs small step.
I want to integrate this function on each coefficient value and returns the entire array of integration as the result (length ~ 10^7), efficiently.
What kind of tools should I use?
(+) I just changed my code from simple example to actual integration form that I need to solve. Sorry for making confusion.
I suspected that this integral would converge for certain values of b and c, so I tried to evaluate this using Sympy:
import sympy
sympy.init_printing()
a, b, c = sympy.symbols('a, b, c', positive=True)
x = sympy.Symbol('x', positive=True)
sympy.integrate(a*(x**(-b))*(sympy.sqrt(x**c+1)-1), (x, 0, sympy.oo))
This means that you should be able to obtain the correct results with this code as long as your coefficients pass the check function.
from numpy import sqrt, pi
from scipy.special import gamma
def check(a, b, c):
assert (-(-b + 1)/c < 1)
assert (1/2 - (-b + 1)/c > 1)
assert (1 - (-b + 1)/c > 1)
def result(a, b, c):
return a*gamma(-b/c + 1 + 1/c)*gamma(b/c - 1/2 - 1/c)/(2*sqrt(pi)*(b - 1))
import math
import pylab
import sympy
def f1(x):
"""function representing a cosine variant function and returned"""
return math.cos(2 * math.pi * x) * math.exp(-x ** 2)
def f2(x):
"""function representing a log variant function and returned"""
return math.log(x + 2.2)
def positive_places(f, xs):
"""return a list of elements of xs that are positive when operated in by
f"""
list1 = []
for i in xs:
if f(i) > 0:
list1.append(i)
return list1
def create_plot_data(f, xmin, xmax, n):
"""returns a tuple (xs, ys) where xs and ys are two sequences,
each containing n numbers"""
xs = [xmin + i * ((xmax - xmin) / (n - 1)) for i in range(n)]
ys = [f(xs[i]) for i in range(n)]
return (xs, ys)
def myplot():
"""plots a graph of f1() and returns the graph"""
print(create_plot_data(f1, -2, 2, 1001))
(a1, b1) = create_plot_data(f1, -2, 2, 1001)
(a2, b2) = create_plot_data(f2, -2, 2, 1001)
pylab.plot(a1, b1, label='f1(x)')
pylab.plot(a2, b2, label='f2(x)')
pylab.xlabel('x')
pylab.ylabel('y')
pylab.legend()
pylab.grid()
pylab.savefig('plot.pdf')
pylab.savefig('plot.png')
pylab.show()
def find_cross():
return sympy.solve(math.cos(2 * math.pi * x) * math.exp(-x ** 2) - math.log(x + 2.2), x)
`
Hi im trying to define a function that will find the positive x value point where 2 equations are equal: f1(x) = f2(x)
f1(x) = math.cos(2 * math.pi * x) * math.e ** (-x ** 2)
f2(x) = math.log(x + 2.2)
for the point between x = 0 and x = 0.5
When using SymPy you can take advantage of mpmath's root finder ability to work with arbitrary many digits:
import mpmath
import sympy as sy
sy.init_printing()
mpmath.mp.dps = 30 # accuracy of 30 digits
# declare expressions:
x = sy.Symbol('x', real=True)
f1 = sy.cos(2*sy.pi*x) * sy.exp(-x**2)
f2 = sy.log(x + sy.Rational(22, 10))
g = f2 - f1 # f1 == f2 is equivalent to g == 0:
dg = g.diff(x) # 1. derivative needed by numeric root-finder
# generate numeric functions from SymPy expressions:
g_m, dg_m = (sy.lambdify(x, f_, modules="mpmath") for f_ in (g, dg))
# find the roots:
x_sln = mpmath.findroot(g_m, [0, 0.5], solver='secant', df=dg)
print("Intersection f1(x) == f2(x) is at x = {}".format(x_sln))
# => Intersection f1(x) == f2(x) is at x = 0.0922612383093564032487110560683
Note that mixing SymPy, math (and NumPy) functions can have unwanted side effects.