local variable referenced before assignment | matplotlib - python

I want to visualize the central limit theorem for an exemplary PDF. The following code works as I wish:
import sympy as sp
import numpy as np
import matplotlib.pyplot as plt
x, y = sp.symbols('x y')
dens = sp.exp(-x) #PDF
def sum(dens,n):
"""
Parameters
----------
dens : probabilty density function
n : amount of iteration
Returns
-------
pdf after n iterations
"""
dens2 = dens.subs(x, y-x) #PDF for one further summed random variable
i=1
while i<=n:
int = sp.integrate(dens*dens2, (x,0,y))
int = int.subs(y,x)
dens = int
i += 1
return int
#plot for n th iteration
n1 = 1
n2 = 20
n3=50
n4 = 100
X1 = np.linspace(0,200,num=1000)
Y1 = sp.lambdify(x,sum(dens,n1),'numpy')
plt.plot(X1, Y1(X1),label="n=1")
X2 = np.linspace(0,200,num=1000)
Y2 = sp.lambdify(x,sum(dens,n2),'numpy')
plt.plot(X2, Y2(X2),label="n=20")
X3 = np.linspace(0,200,num=1000)
Y3 = sp.lambdify(x,sum(dens,n3),'numpy')
plt.plot(X3, Y3(X3),label="n=50")
X4 = np.linspace(0,200,num=1000)
Y4 = sp.lambdify(x,sum(dens,n4),'numpy')
plt.plot(X4, Y4(X4),label="n=100")
plt.legend()
plt.show()
Now I'd like to do the plot for all the n possibilities (later I want to try to animate it, but at first I need to understand how to do this loop). Thus I want to do the plot using a loop instead of creating the plots separately as above. But this gives me the error
Traceback (most recent call last):
File "C:\Users\user\Desktop\ZGS.py", line 71, in
Y = sp.lambdify(x,sum(dens,k),'numpy')
File "C:\Users\user\Desktop\ZGS.py", line 32, in sum
return int
UnboundLocalError: local variable 'int' referenced before assignment
I tried some things such as global int but this creates problems within sympy. Why can I use different variables for n when plotting separately but get this error when assigning n using a loop?
n=100
for k in range(n):
X = np.linspace(0,200,num=1000)
Y = sp.lambdify(x,sum(dens,k),'numpy')
plt.plot(X, Y(X))
plt.show()
How can this problem be solved?

for k in range(n):
...
Y = sp.lambdify(x,sum(dens,k),'numpy')
...
On the first iteration k is zero.
>>> for k in range(3):
... print(k)
...
0
1
2
When dens is called with k == 0 - while i<=n is False and nothing in that while loop is processed. When return int is processed int does not exist.
range takes an optional start argument which would alleviate your error:
>>> for k in range(1,3+1):
... print(k)
...
1
2
3
>>>

Related

Plotting a piecewise function in python with numpy

Note: This is for homework so please don't post full code responses, just help on what I'm misusing would be appreciated
I'm trying to plot a piecewise defined function where when 0 < x <= 10 it will be a constant (KQ/10) and for x > 10 it will be KQ/x for 10 < x < 50. Currently my result comes back as a single value instead of my expected result of an array with a constant value up until x > 10 and then varying values until x = 50
My current code
import matplotlib.pyplot as plt
import scipy
x = np.linspace(0, 50, 1)
R = 10
r = np.linspace(10, 50, 1)
k = 1/(4*np.pi*constants.epsilon_0)
Q = 1
def inside(x):
return k*Q/R
def outer(x):
return k*Q/x
result = np.piecewise(
x,
[x <= R, x > R ],
[lambda x: inside(x), lambda x: outer(x)]
)
result
#plt.plot(x,result)
#plt.axis([0, 50,0, 500000])
#plt.xlabel('x')
#plt.ylabel('y')```
x = np.linspace(0, 50, 1)
isnt how linspace works... this only creates one data point ...
x = np.linspace(0, 50, 10000) ... would create 10k datapoints
perhaps you wanted np.arange(0,50,1) ?

How do I only plot the values I want?

I currently have the code and I having some trouble trying to plot it, I know that trying to plot both ymax and y won't work in this case, but how would I go about plotting just the value for y? I have plotted the function before by removing the ymax from the return, but I need to print the values and plot the solution for y.
import numpy as np
import matplotlib.pyplot as plt
def GaussElimination(A):
'''
Description: Use Gauss elimination to solve a set of simultaneous equations
Parameters: A a matrix of coefficient and constant value for the system
Return: a matrix holding the solution to the equation. This corresponds to the last n
'''
nr,nc=A.shape
B= A.copy()
# start the gauss elimination
for r in range(nr):
#pivoting
max=abs(B[r][r])
maxr = r
for rr in range(r,nr):
if max < abs(B[rr][r]):
max = abs(B[rr][r])
maxr = rr
if max == 0:
print("Singular Matrix")
return []
# swap if needed
if (maxr != r):
for c in range(nc):
temp = B[r][c]
B[r][c]=B[maxr][c]
B[maxr][c] = temp
# scale the row
scale = B[r][r]
for c in range(r,nc):
B[r][c] = B[r][c]/scale
# eliminate values in the columns
for rr in range(nr):
if rr != r:
scale = B[rr][r]
for c in range(r,nc):
B[rr][c]=B[rr][c] - scale*B[r][c]
if (nc == nr+1):
return B[:,nc-1]
else:
return B[:,(nr):nc]
def SimplySupportedBeam(n):
M = np.zeros([n+1,n+1])
C = np.array([[0],[150],[0],[0],[0],[0]])
for r in range(n-3):
M[r][r] = 1
M[r][r+1] = -4
M[r][r+2] = 6
M[r][r+3] = -4
M[r][r+4] = 1
M[n-3][1] = 1
M[n-2][n-1] = 1
M[n-1][n-5] = 1
M[n-1][n-4] = -2
M[n-1][n-3] = 1
M[n][n-2] = 1
M[n][n-1] = -2
M[n][n] = 1
A = np.concatenate((M,C), axis=1)
y0 = GaussElimination(A)
y = y0[1:n]
ymax = np.amax(abs(y))
return y, ymax
n = int(input("Index of the last node: "))
print (SimplySupportedBeam(n))
plt.figure(1)
plt.plot(SimplySupportedBeam(n))
plt.show()
How would I plot just the value I get for y from my code?
It seems like y is 1D numpy array.
If you just want to plot its values against their indices you should be able to do so using either
plt.plot(SimplySupportedBeam(n)[0])
or
y, ymax = SimplySupportedBeam(n)
plt.plot(y)
The problem was that your function returns two values, i.e. y and ymax.
(I did not

Python: Numerov's method plotting error

I'm trying to solve the Schrödinger equation with the Numerov's method. Here is my code:
from pylab import *
from scipy.optimize import brentq
import numpy as np
l = float(input("Angular momentum l:"))
L = float(input("Width of the potential:"))
Vo = float(input("Value of the potential:"))
N = int(input("Number of steps (~10000):"))
h = float(3*L/N)
psi = np.zeros(N) #wave function
psi[0] = 0
psi[1] = h
def V(x,E):
"""
Effective potential function.
"""
if x > L:
return -2*E+l*(l+1)/x**2
else:
return -2*(Vo+E)+l*(l+1)/x**2
def Wavefunction(energy):
"""
Calculates wave function psi for the given value
of energy E and returns value at point xmax
"""
global psi
global E
E=energy
for i in range(2,N):
psi[i]=(2*(1+5*(h**2)*V(i*h,E)/12)*psi[i-1]-(1-(h**2)*V((i-1)*h,E)/12)*psi[i-2])/(1-(h**2)*V((i+1)*h,E)/12)
return psi[-1]
def find_energy_levels(x,y):
"""
Gives all zeroes in y = psi_max, x=en
"""
zeroes = []
s = np.sign(y)
for i in range(len(y)-1):
if s[i]+s[i+1] == 0: #sign change
zero = brentq(Wavefunction, x[i], x[i+1])
zeroes.append(zero)
return zeroes
def main():
energies = np.linspace(-Vo,0,int(10*Vo)) # vector of energies where we look for the stable states
psi_max = [] # vector of wave function at x = 3L for all of the energies in energies
for energy in energies:
psi_max.append(Wavefunction(energy)) # for each energy find the the psi_max at xmax
E_levels = find_energy_levels(energies,psi_max) # now find the energies where psi_max = 0
print ("Energies for the bound states are: ")
for E in E_levels:
print ("%.2f" %E)
# Plot the wavefunctions for first 4 eigenstates
x = np.linspace(0, 3*L, N)
figure()
for E in E_levels:
Wavefunction(E)
plot(x, psi, label="E = %.2f"%E)
legend(loc="upper right")
xlabel('r')
ylabel('$u(r)$', fontsize = 10)
grid()
savefig('numerov.pdf', bbox_inches='tight')
if __name__ == "__main__":
main()
Everything was working really well, this is a plot for Vo=35, l=1, but when I try whit a value of Vo=85, l=0 (is the same for Vo>50), the plot is not what I expected (the end of the plot blows up). For l=1, the error vanish. I am a novice in Python, so I do not know what would be the error. Thanks for the help.

Plot a piecewise function in 3D

I am new to python and trying to 3d plot a piecewise function. I am trying to 3d plot the 'mainformula' function below on the z-axis as it varies with x and y ranging from 0 to 10, and constant = 1. But I can't quite seem to figure out the plotting method here.
from sympy import *
from mpl_toolkits.mplot3d import Axes3D
from matplotlib import cm
from matplotlib.ticker import LinearLocator, FormatStrFormatter
import matplotlib.pyplot as plt
import numpy as np
def mainformula(x,y,constant):
return Piecewise((subformula1(x,y,constant), y >= 0 and y < 3),(subformula2(x,y,constant),y>=3 and y <= 10))
def subformula1(x,y,constant):
return x + y + constant
def subformula2(x,y,constant):
return x - y - constant
fig = plt.figure()
ax = fig.gca(projection='3d')
X = np.arange(0, 10, 0.25)
Y = np.arange(0, 10, 0.25)
constant = 1
X, Y = np.meshgrid(X, Y)
Z = mainformula(X,Y,constant)
surf = ax.plot_surface(X, Y, Z)
plt.show()
The error I get when I run that code is: "ValueError: The truth value of an array with more than one element is ambiguous. Use a.any() or a.all()"
You are working on arrays so it's never going to work to use array > 3 in a boolean context (for example with and) this will always give you the error you received. But you can always define your conditions as boolean masks and operate your formula on the appropriate elements:
def mainformula(x,y,constant):
z = np.zeros_like(x)
# Condition 1 indexes all elements where subformula 1 is valid
condition1 = np.logical_and(y >= 0, y < 3)
# condition1 = (y >= 0) & (y < 3) # is another way of writing it
z[condition1] = x[condition1] + y[condition1] + constant
# now do it in the range where subformula 2 is valid
condition2 = np.logical_and(y >= 3, y <= 10)
# condition1 = (y >= 3) & (y <= 10) # is another way of writing it
z[condition2] = x[condition2] - y[condition2] - constant
return z
This doesn't use sympy.Piecewise but works alright when only trying to plot. If you want seperate functions instead of doing it all in the main formula you need to change it a bit:
z[condition1] = subformula1(x[condition1], y[condition1], constant)
and similar for condition2.
The problem is you are trying to make logic assertions into arrays that do not have a direct one. It's difficult for Python to assert what this is:
y >= 0 and y < 3
So you'll have to change somewhat your code into something it can understand:
def mainformula(x,y,constant):
y2 = y[y>=0]
y2 = y2[y2<3]
y3 = y[y>=3]
y3 = y2[y2<=10]
return Piecewise((subformula1(x,y,constant), y2),(subformula2(x,y,constant),y3))
The problem is Piecewise function also does not seem to accept some arrays into one of the arguments you have. You'll have to rethink your problem, perhaps by building a loop for the Piecewise function in order to be launched at every element.

Correctly annotate a numba function using jit

I started with this code to calculate a simple matrix multiplication. It runs with %timeit in around 7.85s on my machine.
To try to speed this up I tried cython which reduced the time to 0.4s. I want to also try to use numba jit compiler to see if I can get similar speed ups (with less effort). But adding the #jit annotation appears to give exactly the same timings (~7.8s). I know it can't figure out the types of the calculate_z_numpy() call but I'm not sure what I can do to coerce it. Any ideas?
from numba import jit
import numpy as np
#jit('f8(c8[:],c8[:],uint)')
def calculate_z_numpy(q, z, maxiter):
"""use vector operations to update all zs and qs to create new output array"""
output = np.resize(np.array(0, dtype=np.int32), q.shape)
for iteration in range(maxiter):
z = z*z + q
done = np.greater(abs(z), 2.0)
q = np.where(done, 0+0j, q)
z = np.where(done, 0+0j, z)
output = np.where(done, iteration, output)
return output
def calc_test():
w = h = 1000
maxiter = 1000
# make a list of x and y values which will represent q
# xx and yy are the co-ordinates, for the default configuration they'll look like:
# if we have a 1000x1000 plot
# xx = [-2.13, -2.1242,-2.1184000000000003, ..., 0.7526000000000064, 0.7584000000000064, 0.7642000000000064]
# yy = [1.3, 1.2948, 1.2895999999999999, ..., -1.2844000000000058, -1.2896000000000059, -1.294800000000006]
x1, x2, y1, y2 = -2.13, 0.77, -1.3, 1.3
x_step = (float(x2 - x1) / float(w)) * 2
y_step = (float(y1 - y2) / float(h)) * 2
y = np.arange(y2,y1-y_step,y_step,dtype=np.complex)
x = np.arange(x1,x2,x_step)
q1 = np.empty(y.shape[0],dtype=np.complex)
q1.real = x
q1.imag = y
# Transpose y
x_y_square_matrix = x+y[:, np.newaxis] # it is np.complex128
# convert square matrix to a flatted vector using ravel
q2 = np.ravel(x_y_square_matrix)
# create z as a 0+0j array of the same length as q
# note that it defaults to reals (float64) unless told otherwise
z = np.zeros(q2.shape, np.complex128)
output = calculate_z_numpy(q2, z, maxiter)
print(output)
calc_test()
I figured out how to do this with some help from someone else.
#jit('i4[:](c16[:],c16[:],i4,i4[:])',nopython=True)
def calculate_z_numpy(q, z, maxiter,output):
"""use vector operations to update all zs and qs to create new output array"""
for iteration in range(maxiter):
for i in range(len(z)):
z[i] = z[i] + q[i]
if z[i] > 2:
output[i] = iteration
z[i] = 0+0j
q[i] = 0+0j
return output
What I learnt is that use numpy datastructures as inputs (for typing), but within use c like paradigms for looping.
This runs in 402ms which is a touch faster than cython code 0.45s so for fairly minimal work in rewriting the loop explicitly we have a python version faster than C(just).

Categories