Calculating a definite integral with variable boundaries in Python - python

I have been currently working on calculating a definite integral. I need to obtain some numerical outputs, but I cannot. My two attempts to solve the integral are below. How can I run one of these methods below?
My first way to calculate the integral is
import numpy as np
from sympy import *
Nt = 17
alpha = .99
t = np.linspace(0, .85, Nt)
s = np.linspace(0, .85, Nt)
for k in range(Nt):
for i in range(k):
Int = integrate( (t[k] - s) ** - int(alpha), (s, t[i], t[i + 1] ))
print(Int)
Error:
ValueError: Invalid limits given: ((array([0. , 0.053125, 0.10625 , 0.159375, 0.2125 , 0.265625,
0.31875 , 0.371875, 0.425 , 0.478125, 0.53125 , 0.584375,
0.6375 , 0.690625, 0.74375 , 0.796875, 0.85 ]), 0.0, 0.053125),)
My second way is
import numpy as np
from sympy import *
from numba import jit, prange
Nt = 17
alpha = .99
t = np.linspace(0, .85, Nt)
s = np.linspace(0, .85, Nt)
#jit(nopython=True)
def Int(alpha):
Int = 0
for k in prange(Nt):
for i in prange(k):
Int = Int + integrate( (t[k] - s) ** - int(alpha), (s, t[i], t[i + 1] ))
print(Int)
return Int
There is no any outputs. 
EDIT: For my first way, I made a small change and got some numerical results. I am not sure about the result. Any comments are still welcome.
Nt = 17
alpha = .99
t = np.linspace(0, .85, Nt)
s = symbols('s')
for k in range(Nt):
for i in range(k):
Int = integrate((t[k] - s) ** - int(alpha), (s, t[i], t[i + 1] ))
print(Int)
Output:
0.0531250000000000
0.0531250000000000
0.0531250000000000
0.0531250000000000...

If you use SymPy you can get a symbolic expression:
>>> var('s t e ti tj');i=integrate(1/(t-s)**e,(t,ti,tj));i
Piecewise(
((t - ti)**(1 - e)/(1 - e) - (t - tj)**(1 - e)/(1 - e),
(e > -oo) & (e < oo) & Ne(e, 1)),
(log(t - ti) - log(t - tj),
True))
With e = -int(.99) = 0 -- are you sure that is what you want? -- this is
>>> i0 = i.subs(e,-int(.99)); i0
-ti + tj
In this case, the integral does not depend on t, only the difference in the limits. As Oscar says, you can now just substitute in values of interest. Or, knowing that it is just the difference of points, you could compute those differences directly:
>>> ts = [.85/16*_ for _ in range(17)]
>>> i0.subs({ti:ts[0], tj:ts[1]})
0.053125
>>> ts[1] - ts[0]
0.053125
This is the same that you got. You now (hopefully) understand why you were getting the same value every time.

Related

Sympy gives a residual value when trying to solve the logistics equation

If I try solving the logistics differential equation in Sympy I get a residual value (10^(-13)) which prevents sympy from getting the correct values for the initial coditions. If I run this code:
import numpy as np
import sympy as sp
M = 10000
a = 0.03
x = sp.symbols("x")
# x, a, M = sp.symbols("x a M")
f = sp.Function('f')
fl = sp.Derivative(f(x),x)
sol = sp.dsolve(fl - a*(1 - f(x)/M)*f(x), f(x));sol
I get:
Eq(f(x), (9.09494701772928e-13*exp(0.03*C1 - 0.03*x) - 10000.0)/(exp(0.03*C1 - 0.03*x) - 1))
How can one get rid of these residuals in the solution?
Either don't use Float (use a = Rational(3, 100)) or if you know you want those 1e-13 magnitude numbers to be 0 then you can replace them with 0:
>>> eq
Eq(f(x), (9.09494701772928e-13*exp(0.03*C1 - 0.03*x) - 10000.0
... )/(exp(0.03*C1 - 0.03*x) - 1))
>>> eq.replace(lambda x: x.is_Float and abs(x) < 1e-12, lambda x: 0)
Eq(f(x), -10000.0/(exp(0.03*C1 - 0.03*x) - 1))

Finding roots of an equation involving a summation using sympy

I am currently to new to sympy and I am trying to reproduce the Mathematica example in the attached image in Python. My attempt is written below but it returns an empty list
import sympy
m , n, D_star, a, j = sympy.symbols('m , n, D_star, a, j')
s1 = sympy.Sum(a**(j-1),(j, 1, m-1))
rhs = 6 * sympy.sqrt((D_star * (1 + a)*(n - 1))/2)
expand_expr = sympy.solve(s1 - rhs, m)
temp = sympy.lambdify((a, n, D_star), expand_expr, 'numpy')
n = 100
a = 1.2
D_star = 2.0
ms = temp(1.2, 100, 2.0)
ms
# what I get is an empty list []
# expected answer using Mma FindRoot function is 17.0652
Adding .doit() to expand the sum seems to help. It gives Piecewise((m - 1, Eq(a, 1)), ((a - a**m)/(1 - a), True))/a for the sum in s1.
from sympy import symbols, Eq, Sum, sqrt, solve, lambdify
m, n, j, a, D_star = symbols('m n j a D_star')
s1 = Sum(a**(j - 1), (j, 1, m - 1)).doit()
rhs = 6 * sqrt((D_star * (1 + a) * (n - 1)) / 2)
expand_expr = solve(Eq(s1, rhs), m)
temp = lambdify((a, n, D_star), expand_expr, 'numpy')
n = 100
a = 1.2
D_star = 2.0
ms = temp(1.2, 100, 2.0)
This gives for expand_expr:
[Piecewise((log(a*(3*sqrt(2)*a*sqrt(D_star*(a*n - a + n - 1)) - 3*sqrt(2)*sqrt(D_star*(a*n - a + n - 1)) + 1))/log(a), Ne(a, 1)), (nan, True)),
Piecewise((3*sqrt(2)*a*sqrt(D_star*(a*n - a + n - 1)) + 1, Eq(a, 1)), (nan, True))]
which separates into a != 1 and a == 1.
The result of ms gives [array(17.06524172), array(nan)], again in a bit awkward way to separate a hypothetical a == 1.

Overflow and Invalid Values encountered in double scalars - Nonlinear PDE Solving

I am seeking to find a finite difference solution to the 1D Nonlinear PDE
u_t = u_xx + u(u_x)^2
Code:
import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
from matplotlib import cm
import math
'''
We explore three different numerical methods for solving the PDE, with solution u(x, t),
u_t = u_xx + u(u_x)^2
for (x, t) in (0, 1) . (0, 1/5)
u(x, 0) = 40 * x^2 * (1 - x) / 3
u(0, t) = u(1, t) = 0
'''
M = 30
dx = 1 / M
r = 0.25
dt = r * dx**2
N = math.floor(0.2 / dt)
x = np.linspace(0, 1, M + 1)
t = np.linspace(0, 0.2, N + 1)
U = np.zeros((M + 1, N + 1)) # Initial array for solution u(x, t)
U[:, 0] = 40 * x**2 * (1 - x) / 3 # Initial condition (: for the whole of that array)
U[0, :] = 0 # Boundary condition at x = 0
U[-1, :] = 0 # Boundary condition at x = 1 (-1 means end of the array)
'''
Explicit Scheme - Simple Forward Difference Scheme
'''
for q in range(0, N - 1):
for p in range(0, M - 1):
b = 1 / (1 - 2 * r)
C = r * U[p, q] * (U[p + 1, q] - U[p, q])**2
U[p, q + 1] = b * (U[p, q] + r * (U[p + 1, q + 1] + U[p - 1, q + 1]) - C)
T, X = np.meshgrid(t, x)
fig = plt.figure()
ax = fig.gca(projection='3d')
surf = ax.plot_surface(T, X, U)
#fig.colorbar(surf, shrink=0.5, aspect=5) # colour bar for reference
ax.set_xlabel('t')
ax.set_ylabel('x')
ax.set_zlabel('u(x, t)')
plt.tight_layout()
plt.savefig('FDExplSol.png', bbox_inches='tight')
plt.show()
The code I use produces the following error:
overflow encountered in double_scalars
C = r * U[p, q] * (U[p + 1, q] - U[p, q])**2
invalid value encountered in double_scalars
U[p, q + 1] = b * (U[p, q] + r * (U[p + 1, q + 1] + U[p - 1, q + 1]) - C)
invalid value encountered in double_scalars
C = r * U[p, q] * (U[p + 1, q] - U[p, q])**2
Z contains NaN values. This may result in rendering artifacts.
surf = ax.plot_surface(T, X, U)
I've looked up these errors and I assume that the square term generates values too small for the dtype. However when I try changing the dtype to account for a larger range of numbers (np.complex128) I get the same error.
The resulting plot obviously has most of its contents missing. So, my question is, what do I do?
Discretisation expression was incorrect.
Should be
for q in range(0, N - 1):
for p in range(0, M - 1):
U[p, q + 1] = r * (U[p + 1, q] - 2 * U[p, q] + U[p - 1, q]) + r * U[p, q] * (U[p + 1, q] - U[p, q])

Is there a faster way of repeating a chunk of code x times and taking an average?

Starting with:
a,b=np.ogrid[0:n+1:1,0:n+1:1]
B=np.exp(1j*(np.pi/3)*np.abs(a-b))
B[z,b] = np.exp(1j * (np.pi/3) * np.abs(z - b +x))
B[a,z] = np.exp(1j * (np.pi/3) * np.abs(a - z +x))
B[diag,diag]=1-1j/np.sqrt(3)
this produces an n*n grid that acts as a matrix.
n is just a number chosen to represent the indices, i.e. an a*b matrix where a and b both go up to n.
Where z is a constant I choose to replace a row and column with the B[z,b] and B[a,z] formulas. (Essentially the same formula but with a small number added to the np.abs(a-b))
The diagonal of the matrix is given by the bottom line:
B[diag,diag]=1-1j/np.sqrt(3)
where,
diag=np.arange(n+1)
I would like to repeat this code 50 times where the only thing that changes is x so I will end up with 50 versions of the B np.ogrid. x is a randomly generated number between -0.8 and 0.8 each time.
x=np.random.uniform(-0.8,0.8)
I want to generate 50 versions of B with random values of x each time and take a geometric average of the 50 versions of B using the definition:
def geo_mean(y):
y = np.asarray(y)
return np.prod(y ** (1.0 / y.shape[0]), axis=-1)
I have tried to set B as a function of some index and then use a for _ in range(): loop, this doesn't work. Aside from copy and pasting the block 50 times and denoting each one as B1, B2, B3 etc; I can't think of another way of working this out.
EDIT:
I'm now using part of a given solution in order to show clearly what I am looking for:
#A matrix with 50 random values between -0.8 and 0.8 to be used in the loop
X=np.random.uniform(-0.8,0.8, (50,1))
#constructing the base array before modification by random x values in position z
a,b = np.ogrid[0:n+1:1,0:n+1:1]
B = np.exp(1j * ( np.pi / 3) * np.abs( a - b ))
B[diag,diag] = 1 - 1j / np.sqrt(3)
#list to store all modified arrays
randomarrays = []
for i in range( 0,50 ):
#copy array and modify it
Bnew = np.copy( B )
Bnew[z, b] = np.exp( 1j * ( np.pi / 3 ) * np.abs(z - b + X[i]))
Bnew[a, z] = np.exp( 1j * ( np.pi / 3 ) * np.abs(a - z + X[i]))
randomarrays.append(Bnew)
Bstack = np.dstack(randomarrays)
#calculate the geometric mean value along the axis that was the row in 2D arrays
B0 = geo_mean(Bstack)
From this example, every iteration of i uses the same value of X, I can't seem to get a way to get each new loop of i to use the next value in the matrix X. I am unsure of the ++ action in python, I know it does not work in python, I just don't know how to use the python equivalent. I want a loop to use a value of X, then the next loop to use the next value and so on and so forth so I can dstack all the matrices at the end and find a geo_mean for each element in the stacked matrices.
One pedestrian way would be to use a list comprehension or generator expression:
>>> def f(n, z, x):
... diag = np.arange(n+1)
... a,b=np.ogrid[0:n+1:1,0:n+1:1]
... B=np.exp(1j*(np.pi/3)*np.abs(a-b))
... B[z,b] = np.exp(1j * (np.pi/3) * np.abs(z - b +x))
... B[a,z] = np.exp(1j * (np.pi/3) * np.abs(a - z +x))
... B[diag,diag]=1-1j/np.sqrt(3)
... return B
...
>>> X = np.random.uniform(-0.8, 0.8, (10,))
>>> np.prod((*map(np.power, map(f, 10*(4,), 10*(2,), X), 10 * (1/10,)),), axis=0)
But in your concrete example we can do much better than that;
using the identity exp(a) x exp(b) = exp(a + b) we can convert the geometric mean after exponentiation to an arithmetic mean before exponentition. A bit of care is required because of the multivaluedness of the complex n-th root which occurs in the geometric mean. In the code below we normalize the angles occurring to range -pi, pi so as to always hit the same branch as the n-th root.
Please also note that the geo_mean function you provide is definitely wrong. It fails the basic sanity check that taking the average of copies of the same thing should return the same thing. I've provided a better version. It is still not perfect, but I think there actually is no perfect solution, because of the nonuniqueness of the complex root.
Because of this I recommend taking the average before exponentiating. As long as your random spread is less than pi this allows a well-defined averaging procedure with an average that is actually close to the samples
import numpy as np
def f(n, z, X, do_it_pps_way=True):
X = np.asanyarray(X)
diag = np.arange(n+1)
a,b=np.ogrid[0:n+1:1,0:n+1:1]
B=np.exp(1j*(np.pi/3)*np.abs(a-b))
X = X.reshape(-1,1,1)
if do_it_pps_way:
zbx = np.mean(np.abs(z-b+X), axis=0)
azx = np.mean(np.abs(a-z+X), axis=0)
else:
zbx = np.mean((np.abs(z-b+X)+3) % 6 - 3, axis=0)
azx = np.mean((np.abs(a-z+X)+3) % 6 - 3, axis=0)
B[z,b] = np.exp(1j * (np.pi/3) * zbx)
B[a,z] = np.exp(1j * (np.pi/3) * azx)
B[diag,diag]=1-1j/np.sqrt(3)
return B
def geo_mean(y):
y = np.asarray(y)
dim = len(y.shape)
y = np.atleast_2d(y)
v = np.prod(y, axis=0) ** (1.0 / y.shape[0])
return v[0] if dim == 1 else v
def geo_mean_correct(y):
y = np.asarray(y)
return np.prod(y ** (1.0 / y.shape[0]), axis=0)
# demo that orig geo_mean is wrong
B = np.exp(1j * np.random.random((5, 5)))
# the mean of four times the same thing should be the same thing:
if not np.allclose(B, geo_mean([B, B, B, B])):
print('geo_mean failed')
if np.allclose(B, geo_mean_correct([B, B, B, B])):
print('but geo_mean_correct works')
n, z, m = 10, 3, 50
X = np.random.uniform(-0.8, 0.8, (m,))
B0 = f(n, z, X, do_it_pps_way=False)
B1 = np.prod((*map(np.power, map(f, m*(n,), m*(z,), X), m * (1/m,)),), axis=0)
B2 = geo_mean_correct([f(n, z, x) for x in X])
# This is the recommended way:
B_recommended = f(n, z, X, do_it_pps_way=True)
print()
print(np.allclose(B1, B0))
print(np.allclose(B2, B1))
I think you should rely more on numpy functionality, when approaching your problem. Not a numpy expert myself, so there is surely room for improvement:
from scipy.stats import gmean
n = 2
z = 1
a = np.arange(n + 1).reshape(1, n + 1)
#constructing the base array before modification by random x values in position z
B = np.exp(1j * (np.pi / 3) * np.abs(a - a.T))
B[a, a] = 1 - 1j / np.sqrt(3)
#list to store all modified arrays
random_arrays = []
for _ in range(50):
#generate random x value
x=np.random.uniform(-0.8, 0.8)
#copy array and modify it
B_new = np.copy(B)
B_new[z, a] = np.exp(1j * (np.pi / 3) * np.abs(z - a + x))
B_new[a, z] = np.exp(1j * (np.pi / 3) * np.abs(a - z + x))
random_arrays.append(B_new)
#store all B arrays as a 3D array
B_stack = np.stack(random_arrays)
#calculate the geometric mean value along the axis that was the row in 2D arrays
geom_mean_for_rows = gmean(B_stack, axis = 2)
It uses the geometric mean function from scipy.stats module to have a vectorised approach for this calculation.

Python: double integration on an infinite domain

I am trying to integrate my function over u and xx and then store the values in a matrix so I can plot them with imshow or pcolormesh. The bounds on the integration are 0 < u < inf and -inf < xx < inf. At the moment, I am only taking the bounds to be 10 until I can figure this out.
import numpy as np
import pylab as pl
from scipy.integrate import dblquad
b = 50.0
x = np.linspace(-10, 10, 1000)
y = np.linspace(0, 10, 1000)
T = pl.zeros([len(x), len(y)])
for xi in enumerate(x):
for yi in enumerate(y):
def f(xi, yi, u, xx):
return ((np.exp(u * (b - yi)) - np.exp(-u * (b - yi))) /
(np.exp(u * b) - np.exp(-u * b)) * np.cos(u * (xx - xi)))
def fint(u, xx):
return T + dblquad(f, -10, 10, 0.1, 10, args = (u, xx))[0]
This is the code I have so far but I know it isn't working properly; unfortunately, I don't know what the problem is. Maybe I can't have the two for loops in my definition of f or my my fint is wrong.
It's not completely clear from your question what you're trying to do. But here's how I interpreted it: You have a double integral over two variables u and xx, which also takes two parameters xi and yi. You want to evaluate the integral over xx and u at many different values of xi and yj, and store these values in T. Assuming this is what you want to do (and correct me if I'm wrong), here's how I would do it.
import numpy as np
from scipy.integrate import dblquad
b = 50.0
x = np.linspace(-10, 10, 1000)
y = np.linspace(0, 10, 1000)
def f(xx, u, xi, yj):
return ((np.exp(u * (b - yj)) - np.exp(-u * (b - yj))) /
(np.exp(u * b) - np.exp(-u * b)) * np.cos(u * (xx - xi)))
T = np.zeros([len(x), len(y)])
for i, xi in enumerate(x):
for j, yj in enumerate(y):
T[i, j] += dblquad(
f, -10, 10, lambda x: 0.1, lambda x: 10, args=(xi, yj))[0]
fint is the only thing that calls f. You are not calling fint, which means that f is not being used at all, just defined about a million times. I would consider defining the function just once and calling it a million times instead.

Categories

Resources