How to carry over variables from past defined functions? - python

I am writing a program that operates out of a main() function. I am unable to change the main function (this is part of a class). How would I go about carrying over a variable from one function to the next, without changing the main()?
def read_data(fname) :
file = fname
x = []
y = []
with open(file) as f:
for line in f:
xi, yi = [float(x) for x in line.split(",")]
x.append(xi)
y.append(yi)
return x, y
def compute_m_and_b(x, y) :
sx, sy, sx2, sxy, sy2 = 0, 0, 0, 0, 0
for i in range(len(x)):
sx += x[i]
sy += y[i]
sx2 += (x[i] ** 2)
sy2 += (y[i] ** 2)
sxy += (x[i] * y[i])
m = (sxy * len(x) - sx * sy) / (sx2 * len(x) - sx**2)
b = (sy - m * sx) / len(x)
return m, b
def compute_fx_residual(x, y, m, b) :
fx = []
for xi in x:
fx.append(m * xi + b)
residual = []
for i in range(len(y)):
residual.append(y[i] - fx[i])
return fx, residual
def compute_sum_of_squared_residuals(residual) :
least_squares_r = 0
for i in range(len(y)) :
least_squares_r += (residual[i]) ** 2
return least_squares_r
def compute_total_sum_of_squares(y) :
sum_squares = 0
ymean = sum(y) / len(y)
for i in range(len(y)) :
sum_squares += (yi - ymean) ** 2
return sum_squares
as you can see, I am restricted to only pulling the variables listed in the parentheses of the def functions. This leads to variables calculated in prior functions being undefined. How can I import them without needing to change the main()?
Please let me know if I should be more clear. I can provide more examples, but I wanted to maintain some brevity.
EDIT: here is the main function:
def main():
fname = input("Enter Input Filename: ")
x, y = regress.read_data(fname)
print()
print("Input File: ", fname)
print("Data points: ", len(x))
#compute coefficients m and b
m, b = regress.compute_m_and_b(x, y)
#compute fx and residual
fx, residual = regress.compute_fx_residual(x, y, m, b)
#compute sum of squared residuals
least_squares_r = regress.compute_sum_of_squared_residuals(residual)
#compute sum of squares
sum_squares = regress.compute_total_sum_of_squares(y)
#compute coefficeint of determination
coeff_of_d = regress.compute_coeff_of_determination(least_squares_r, sum_squares)
regress.print_least_squares(x, y, m, b, fx, residual, least_squares_r, sum_squares, coeff_of_d)
#compute pearson coefficient
pearson_r = regress.compute_pearson_coefficient(x, y)
regress.print_pearson(pearson_r)
return
main()

You haven't provided the main function so it's unclear how you're currently using it.
Looks to me like you can just get the variable for each consecutive function and pass them into the next:
fname = "some/path/to/file.txt"
x, y = read_data(fname)
m, b = compute_m_and_b(x, y, m, b)
fx, residual = compute_fx_residual(x, y, m, b)
least_squares_r = compute_sum_of_squared_residuals(residual)
sum_squares = compute_total_sum_of_squares(y)

Related

Numerical optimization with Gradient Descent in Python

I'm trying to solve the next numerical optimization problem: find the vector x such that minimizes the cost function 0.5 * norm(Bx - v, 2)^2, where B is matrix and v is a vector. I have implemented two gradient descent algorithms. In one of them I manually tune the step-size, and in the other I calculate it automatically with equation (2.5) from ftp://lsec.cc.ac.cn/pub/yyx/papers/p0504.pdf. The gradient of the cost function is B^T(B*x - v).
Additionally, I compare my implementations with the solve(A, B) function from numpy.linalg, noting that the solution of the optimization problem is the solution of the linear system A*x = b, where A = B^T * B, b = B^T * v. So far, I'm getting poor results: large errors and long running times. I don't know it there is an error in my implementation or this is how these algorithms work in the computational experiments that I set up.
In the computational experiments, I generate random "solution" vectors x, and matrices B. Then compute A and b accordingly.
Any feedback is appreciated.
This is my code:
import numpy as np
import matplotlib.pyplot as plt
from numpy import linalg as LA
import time
def residue(x, B, v):
aux = np.dot(B, x) - v
aux = pow(LA.norm(aux, 2), 2)
aux = aux / pow(LA.norm(v, 2), 2)
return aux
def gradGD(x, B, v):
aux = np.dot(B, x) - v
return np.dot(B.T, aux)
def gradientDescent(B, v, alpha, tol, x0):
A = np.dot(B.T, B)
b = np.dot(B.T, v)
x = x0
while True:
res = residue(x, B, v)
print('Residue ', res)
if (res < tol):
break
x = x - alpha * gradGD(x, B, v)
return x
# Gradient descent with auto step-size
def gradientDescentBB(B, v, tol, x0):
x = x0
xpre = np.zeros((N, 1))
flag = 0
while True:
res = residue(x, B, v)
#print('Residue ', res)
if (res < tol):
break
if (flag == 0):
grad = gradGD(x, B, v)
x = x - (1e-06) * grad
flag = 1
continue
gradpre = grad
grad = gradGD(x, B, v)
y = grad - gradpre
s = x - xpre
# print('dot', np.dot(s.T, y))
# print('||y||_2 = ', LA.norm(y, 2))
alpha = np.dot(s.T, y) / pow(LA.norm(y, 2), 2)
# print("alpha = ", alpha)
xpre = x
x = x - alpha * grad
return x
# Solves the optimization problem via Ax * b
def solver(B, v):
A = np.dot(B.T, B)
b = np.dot(B.T, v)
return np.linalg.solve(A, b)
# Main routine
N = 1000
epsilon = 1.0e-6
a = 1/N - epsilon
iter = 20
mytime_iter = []
time2_iter = []
myeabs_iter = []
myerel_iter = []
myepercent_iter = []
cgseabs_iter = []
cgserel_iter = []
cgsepercent_iter = []
# Running the experiment many times
for i in range(iter):
print('Iteration: ', i)
B = a * np.random.randn(N, N) + np.ones((N, N))
#print(B)
x0 = np.random.randn(N, 1) # Real solution of the optmization problem
v = np.dot(B, x0)
mystart = time.time()
# x = gradientDescent(B, v, alpha=1999100e-09, tol=1e-05, x0=np.zeros((N, 1))) # Gradient Descent: Method 1
x = gradientDescentBB(B, v, tol=1e-05, x0=np.zeros((N, 1))) # Gradient Descent: Method 2
myend = time.time()
mytime = myend - mystart
start2 = time.time()
xalt = solver(B, v) # Solution of the optimization problem by solving A*x = b
end2 = time.time()
time2 = start2 - end2
myeabs = LA.norm(x - x0, 2)
myerel = myeabs / LA.norm(x0, 2)
myepercent = myerel * 100
cgseabs = LA.norm(xalt - x0, 2)
cgserel = cgseabs / LA.norm(x0, 2)
cgsepercent = cgserel * 100
mytime_iter.append(mytime)
time2_iter.append(time2)
myeabs_iter.append(myeabs)
myerel_iter.append(myerel)
myepercent_iter.append(myepercent)
cgseabs_iter.append(cgseabs)
cgserel_iter.append(cgserel)
cgsepercent_iter.append(cgsepercent)
plt.figure(1)
plt.plot(mytime_iter, 'bo', label="GD")
plt.plot(time2_iter, 'ro', label="solve()")
plt.legend(loc="upper right")
plt.xlabel("# Iteration")
plt.ylabel("Time (s)")
# plt.ylim(-1.5, 2.0) --
plt.figure(2)
plt.plot(myeabs_iter, "-b", label="GD")
plt.plot(cgseabs_iter, "-r", label="solve()")
plt.legend(loc="upper right")
plt.xlabel("# Iteration")
plt.ylabel("Absolute error")
plt.figure(3)
plt.plot(myerel_iter, "-b", label="GD")
plt.plot(cgserel_iter, "-r", label="solve()")
plt.legend(loc="upper right")
plt.xlabel("# Iteration")
plt.ylabel("Relative error")
plt.figure(4)
plt.plot(myepercent_iter, "-b", label="GD")
plt.plot(cgsepercent_iter, "-r", label="solve()")
plt.legend(loc="upper right")
plt.ylabel("Relative error (%)")
plt.show()

Index Error : Index 2 is out of bounds for axis 0 with size 2

I'm trying to solve some ODE's using different methods and then printing and plotting my results. When I try to run it I get the error IndexError: index 2 is out of bounds for axis 0 with size 2
I know it has to do with the fact of the dimensions, but I thought that all of my dimensions were correct. Here is an example of each way I'm trying to solve the ode's
def f(t,x,y):
xprime = x - y + (2*t) - (t**2) - (t**3)
return xprime
def g(t,x,y):
yprime = x + y - (4*(t**2)) + (t**3)
return yprime
#Exact Solution
def exact(t):
y = np.zeros(len(t))
x = np.zeros(len(t))
for i in range(n):
cos_arr = np.cos(t)
sin_arr = np.sin(t)
y = np.exp(t) * cos_arr + t**2
x = np.exp(t) * sin_arr - t**3
return x, y
#Explicit Euler
def Eulerx(t0, tmax, x0, n):
t, dt = np.linspace(t0, tmax, n, retstep = True)
x = np.zeros(n)
y = np.zeros(n)
x[0] = x0
y[0] =y0
for i in range (n-1):
x[i+1] = x[i] + (dt/2) * f(t[i], x[i], y[i])
return t, x
#RK2
def RK2x(t0, tmax, x0, n):
t, dt = np.linspace(t0, tmax, n, retstep = True)
x = np.zeros(n)
y = np.zeros(n)
x[0] = x0
y[0]=y0
for i in range(n-1):
xK1 = f(t[i], x[i],y[i])
xK2 = f(t[i]+ dt, x[i] +dt * xK1, y[i])
x[i+1] = x[i] +(dt* (1/2)*(xK1 + xK2))
return t, x
#Classical RK4
def RK4x(t0, tmax, x0, n):
t, dt = np.linspace(t0, tmax, n, retstep = True)
x = np.zeros(n)
y = np.zeros(n)
x[0] = x0
y[0] =y0
for i in range(n-1):
x4K1 = f(t[i],x[i],y[i])
x4K2 = f(t[i]+((1/2)*dt), x[i]+ ((1/2)*dt*x4K1),y[i])
x4K3 = f(t[i] +((1/2)*dt), x[i] + ((1/2)*dt*x4K2),y[i])
x4K4 = f(t[i]+dt, x[i]+dt*x4K3,y[i])
x[i+1] = x[i] + (dt*(1/6)*(x4K1 + (2* x4K2) +(2*x4K3) +x4K4))
return t, x
if __name__ == '__main__':
t0 = 0
tmax = 1
x0 = 1
y0 = 0
n=50
[t,X1] = Eulerx(t0,tmax, x0,n)
[t,Y1] = Eulery(t0,tmax, y0,n)
[t, X2]= RK2x(t0,tmax, x0,n)
[t, Y2]= RK2y(t0,tmax, y0,n)
[t, X3]= RK4x(t0,tmax, x0,n)
[t, Y3]= RK4y(t0,tmax, y0,n)
x=exact(t)
y=exact(t)
abs_errx1= abs(x-X1)
abs_errx2= abs(x-X2)
abs_errx3= abs(x-X3)
print("=========================================================================")
print(" n Eulerx Eulery RK2x RK2y RK4x RK4y", end='\n')
for i in range(n):
print(abs_errx1[i], abs_erry1[i], abs_errx2[i], abs_erry2[i], abs_errx3[i], abs_erry3[i])
print("=========================================================================")
Your arrays abs_errx1, etc, are all size (2, 50). You are looking at abs_errx1[n], etc where n runs from 0 to 50. n is being used as the first dimension when you need it to be the second. I'm not sure what the first dimension is supposed to be.

What is overflow encountered in double_scalars?

I'm trying to implement gradient descent in Simple linear regression. Whenever I run the code, I get the error
The code i am using is this:
def get_data(df, feature, predict):
X = df[feature]
Y = df[predict]
X = np.float64(X)
Y = np.float64(Y)
return X, Y
def average(X, Y, b, m, length):
temp1 = 0
temp2 = 0
for i in range(length):
temp1 += (b + m * X[i]) - Y[i]
temp2 += ((b + m * X[i]) - Y[i]) * X[i]
return temp1 / float(length), temp2 / float(length)
def gradient_descent(b, m, alpha, length, num_iterations, X, Y):
for i in range(num_iterations):
temp1, temp2 = average(X, Y, b, m, length)
b_temp = b - alpha * temp1
m_temp = m - alpha * temp2
b = b_temp
m = m_temp
return b, m
def run(b, m, alpha, feature, predict, df, num_iterations):
X, Y = get_data(df, feature, predict)
length = np.alen(X)
final_b, final_m = gradient(b, m, alpha, length, num_iterations, X, Y)
return final_b, final_m
b, m = run(0, 0, 0.05, 'sqft_living', 'price', df, 1000)
The error it gives my is this:
/Users/*****/anaconda2/lib/python2.7/site-packages/ipykernel_launcher.py:15:
RuntimeWarning: overflow encountered in double_scalars from
ipykernel import kernelapp as app
/Users/*****/anaconda2/lib/python2.7/site-packages/ipykernel_launcher.py:25:
RuntimeWarning: invalid value encountered in double_scalars.
I'm not able to identify which part of the code is causing the error. I tried to convert numpy array to float64 also my code is not running into Divide by Zero Error. Can someone identify the error? Also, How can it be rectified?

Need basic understanding of python arguments in order to be able code system of ODE's solution

I am an old Fortran programmer and need to help a young person to numerically solve an ODE system using Heun's method. He only knows Python so I have to learn the minimum python to get this done.
Here is what I came up with. The test code is for a simple 2 degree of freedom system with exponential growth for each degree of freedom.
The error I get is:
Traceback (most recent call last):
File "program.py", line 28, in <module>
heun (imax, y, dt, t)
File "program.py", line 7, in heun
rhs(y, ydot)
NameError: global name 'ydot' is not defined
Here is the code:
# Test RHS of ODE system:
def rhs (y, ydot):
ydot[0] = y[0]
ydot[1] = y[1]
return;
# Does one step of Heun's method:
def heun (imax, y, dt, t):
rhs(y, ydot)
for i in range(0, imax):
y_tilde[i] = y[i] + dt * ydot[i]
rhs(y_tilde, ydot_at_tilde)
for i in range(0, imax):
y[i] = y[i] + dt/2 * (ydot[i] + ydot_at_tilde[i])
t = t + dt
return;
# Initial condition
y = [0, 0]
t = 0
dt = 0.01
nsteps = 100
imax = 1
istep = 1
while istep <= nsteps:
heun (imax, y, dt, t)
print istep, y[0], y[1]
istep = istep + 1
Question: Why does python think that the object ydot in routine heun is global? Even if it were global, why can't I pass it as an argument?
The problem is here:
def heun(imax, y, dt, t):
rhs(y, ydot)
You're calling your rhs function, with the input arguments y and ydot. But ydot doesn't exist inside the scope of your heun function. Only imax, y, dt and t do.
Similarly you never define the variables ydot_tilde or ydot_at_tilde
Also, you're going to need your functions to return some values.
OK thank-you all. Here is a code that works with comments to indicate what I learnt:
def rhs (y, ydot):
ydot[0] = y[0]
ydot[1] = y[1]
return ydot;
def heun (ndof, dt, y, t):
# These initializations of local arrays take the place of Fortran declarations:
ydot = [0] * (ndof)
y_tilde = [0] * (ndof)
ydot_at_tilde = [0] * (ndof)
ydot = rhs(y, ydot)
# Note: In python range means:
# range (first element, upto but not including last element)
for i in range(0, ndof):
y_tilde[i] = y[i] + dt * ydot[i]
ydot_at_tilde = rhs(y_tilde, ydot_at_tilde)
for i in range(0, ndof):
y[i] = y[i] + dt/2 * (ydot[i] + ydot_at_tilde[i])
t = t + dt
# Note: This lists the output arguments:
return y, t;
# Initial condition
y = [1, 1]
t = 0
dt = 0.01
nsteps = 100
ndof = 2
istep = 1
while istep <= nsteps:
# Note: This is how you get the output arguments:
y, t = heun (ndof, dt, y, t)
istep = istep + 1
print t, y[0], y[1]
This is what I would do:
import numpy
def heun(ndof, dt, y, t):
ydot = numpy.zeros(ndof)
y_tilde = numpy.zeros(ndof)
ydot_at_tilde = numpy.zeros(ndof)
# Replacing first two elements does not need a function `rhs()`
ydot[:1] = y[:1]
# Vectorized operation, numpy does this loop for you at C speeds
y_tilde = y + dt * ydot
ydot_at_tilde[:1] = y_tilde[:1]
y = y + dt/2 * (ydot + ydot_at_tilde)
t = t + dt
return y, t
y = numpy.ones(2)
t = 0
dt = 0.01
nsteps = 100
ndof = 2
for istep in range(nsteps):
y, t = heun(ndof, dt, y, t)
print(t, y[0], y[1])
return; From this it is clear that you're modifying the ydot but not returning anything.
And in rhs(y, ydot) you're passing in ydot without first specifying what it should be. Hence declare a function variable ydot first with ydot = [] and hold the result in ydot as ydot = rhs(y,ydot) in heun and use
ydot.append(y[0])
ydot.append(y[1])
in 'rhs'.
return ydot in rhs and return y in heun.
Fix all the not defined errors in a similar manner.

Replacing multiprocessing pool.map with mpi4py

I'm a beginner in using MPI, and I'm still going through the documentation. However, there's very little to work on when it comes to mpi4py. I have written a code that currently uses the multiprocessing module to run on many cores, but I need replace this with mpi4py so that I can use more than one node to run my code. My code is below, when using the multiprocessing module, and also without.
With multiprocessing,
import numpy as np
import multiprocessing
start_time = time.time()
E = 0.1
M = 5
n = 1000
G = 1
c = 1
stretch = [10, 1]
#Point-Distribution Generator Function
def CDF_inv(x, e, m):
A = 1/(1 + np.log(m/e))
if x == 1:
return m
elif 0 <= x <= A:
return e * x / A
elif A < x < 1:
return e * np.exp((x / A) - 1)
#Elliptical point distribution Generator Function
def get_coor_ellip(dist=CDF_inv, params=[E, M], stretch=stretch):
R = dist(random.random(), *params)
theta = random.random() * 2 * np.pi
return (R * np.cos(theta) * stretch[0], R * np.sin(theta) * stretch[1])
def get_dist_sq(x_array, y_array):
return x_array**2 + y_array**2
#Function to obtain alpha
def get_alpha(args):
zeta_list_part, M_list_part, X, Y = args
alpha_x = 0
alpha_y = 0
for key in range(len(M_list_part)):
z_m_z_x = X - zeta_list_part[key][0]
z_m_z_y = Y - zeta_list_part[key][1]
dist_z_m_z = get_dist_sq(z_m_z_x, z_m_z_y)
alpha_x += M_list_part[key] * z_m_z_x / dist_z_m_z
alpha_y += M_list_part[key] * z_m_z_y / dist_z_m_z
return (alpha_x, alpha_y)
#The part of the process containing the loop that needs to be parallelised, where I use pool.map()
if __name__ == '__main__':
# n processes, scale accordingly
num_processes = 10
pool = multiprocessing.Pool(processes=num_processes)
random_sample = [CDF_inv(x, E, M)
for x in [random.random() for e in range(n)]]
zeta_list = [get_coor_ellip() for e in range(n)]
x1, y1 = zip(*zeta_list)
zeta_list = np.column_stack((np.array(x1), np.array(y1)))
x = np.linspace(-3, 3, 100)
y = np.linspace(-3, 3, 100)
X, Y = np.meshgrid(x, y)
print len(x)*len(y)*n,'calculations to be carried out.'
M_list = np.array([.001 for i in range(n)])
# split zeta_list, M_list, X, and Y
zeta_list_split = np.array_split(zeta_list, num_processes, axis=0)
M_list_split = np.array_split(M_list, num_processes)
X_list = [X for e in range(num_processes)]
Y_list = [Y for e in range(num_processes)]
alpha_list = pool.map(
get_alpha, zip(zeta_list_split, M_list_split, X_list, Y_list))
alpha_x = 0
alpha_y = 0
for e in alpha_list:
alpha_x += e[0] * 4 * G / (c**2)
alpha_y += e[1] * 4 * G / (c**2)
print("%f seconds" % (time.time() - start_time))
Without multiprocessing,
import numpy as np
E = 0.1
M = 5
G = 1
c = 1
M_list = [.1 for i in range(n)]
#Point-Distribution Generator Function
def CDF_inv(x, e, m):
A = 1/(1 + np.log(m/e))
if x == 1:
return m
elif 0 <= x <= A:
return e * x / A
elif A < x < 1:
return e * np.exp((x / A) - 1)
n = 1000
random_sample = [CDF_inv(x, E, M)
for x in [random.random() for e in range(n)]]
stretch = [5, 2]
#Elliptical point distribution Generator Function
def get_coor_ellip(dist=CDF_inv, params=[E, M], stretch=stretch):
R = dist(random.random(), *params)
theta = random.random() * 2 * np.pi
return (R * np.cos(theta) * stretch[0], R * np.sin(theta) * stretch[1])
#zeta_list is the list of coordinates of a distribution of points
zeta_list = [get_coor_ellip() for e in range(n)]
x1, y1 = zip(*zeta_list)
zeta_list = np.column_stack((np.array(x1), np.array(y1)))
#Creation of a X-Y Grid
x = np.linspace(-3, 3, 100)
y = np.linspace(-3, 3, 100)
X, Y = np.meshgrid(x, y)
def get_dist_sq(x_array, y_array):
return x_array**2 + y_array**2
#Calculation of alpha, containing the loop that needs to be parallelised.
alpha_x = 0
alpha_y = 0
for key in range(len(M_list)):
z_m_z_x = X - zeta_list[key][0]
z_m_z_y = Y - zeta_list[key][1]
dist_z_m_z = get_dist_sq(z_m_z_x, z_m_z_y)
alpha_x += M_list[key] * z_m_z_x / dist_z_m_z
alpha_y += M_list[key] * z_m_z_y / dist_z_m_z
alpha_x *= 4 * G / (c**2)
alpha_y *= 4 * G / (c**2)
Basically what my code does is, it first generates a list of points that follow a certain distribution. Then I apply an equation to obtain the quantity 'alpha' using different relations between the distances of the points. The part that requires parallelisation is the single for loop involved in the calculation of alpha. What I want to do is to use mpi4py instead of multiprocessing to do this, and I am not sure how to get this going.
Transforming the multiprocessing.map version to MPI can be done using scatter / gather. In your case it is useful, that you already prepare the input list into one chunk for each rank. The main difference is, that all code gets executed by all ranks in the first place, so you must make everything that should be done only by the maste rank 0 conidtional.
if __name__ == '__main__':
comm = MPI.COMM_WORLD
if comm.rank == 0:
random_sample = [CDF_inv(x, E, M)
for x in [random.random() for e in range(n)]]
zeta_list = [get_coor_ellip() for e in range(n)]
x1, y1 = zip(*zeta_list)
zeta_list = np.column_stack((np.array(x1), np.array(y1)))
x = np.linspace(-3, 3, 100)
y = np.linspace(-3, 3, 100)
X, Y = np.meshgrid(x, y)
print len(x)*len(y)*n,'calculations to be carried out.'
M_list = np.array([.001 for i in range(n)])
# split zeta_list, M_list, X, and Y
zeta_list_split = np.array_split(zeta_list, comm.size, axis=0)
M_list_split = np.array_split(M_list, comm.size)
X_list = [X for e in range(comm.size)]
Y_list = [Y for e in range(comm.size)]
work_list = list(zip(zeta_list_split, M_list_split, X_list, Y_list))
else:
work_list = None
my_work = comm.scatter(work_list)
my_alpha = get_alpha(my_work)
alpha_list = comm.gather(my_alpha)
if comm.rank == 0:
alpha_x = 0
alpha_y = 0
for e in alpha_list:
alpha_x += e[0] * 4 * G / (c**2)
alpha_y += e[1] * 4 * G / (c**2)
This works fine as long as each processor gets a similar amount of work. If communication becomes an issue, you might want to split up the data generation among processors instead of doing it all on the master rank 0.
Note: Some things about the code are bogus, e.g. alpha_[xy] ends up as np.ndarray. The serial version runs into an error.
For people who are still interested in similar subjects, I highly recommend having a look at the MPIPoolExecutor() class here and the documentation is here.

Categories

Resources