Locally bind functions within lambda's - python

Is there a way to locally bind functions within lambdas? I have a loop, and within each loop, I create an array of functions. I want to create another function which is the sum of all of these functions and store it in another array. This new array should hold the sum of all functions for each loop.
Then I want to create another function that is the sum over all the sum of functions.
However, the problem I have is that the original functions keep updating, so I am not getting my desired result. Can I locally bind the functions? Am I approaching this the wrong way?
import numpy as np
lmax = 4
lapprox = []
# Function to estimate
def curve_func(x):
return np.sin(x*np.pi)*x**2
# Initialise residual function
def residual_func(x):
return curve_func(x)
# For each l, create 2**l+1 nodes and determine new nodes.
for l in range(lmax):
nodes = np.linspace(0, 1, 2**l+1, endpoint = True)
if (l==0):
old_nodes = nodes
new_nodes = nodes
else:
old_nodes = np.linspace(0, 1, 2**(l-1)+1, endpoint = True)
new_nodes = [x for x in nodes if x not in old_nodes]
# Create basis function corresponding to each new node
if (l==0):
phi = [lambda x, i=i: max(1 - abs(2**l * x - i), 0) for i in range(len(new_nodes))]
else:
phi = [lambda x, i=i: max(1 - abs(2**l * x - (2*i+1)), 0) for i in range(len(new_nodes))]
# Calculate hierarchical surpluses
coeff = [residual_func(n) for n in new_nodes]
# Array of functions: coeff*phi
coeff_phi = [lambda x, func=func, alpha=alpha: coeff[alpha]*func(x) for alpha, func in enumerate(phi)]
# Array of length lmax, where each value is sum of functions in coeff_phi for fixed l
lapprox.append(lambda x: sum(f(x) for f in coeff_phi))
# Sum of all functions in lapprox
totapprox = lambda x: sum(f(x) for f in lapprox)
# Compute new residual function
residual_func = lambda x: curve_func(x) - totapprox(x)
Extra detail on what the code is doing: The code is designed to approximate a function, such as sin(pi*x)*x^2 using hierarchical linear splines. For each level l, there are some basis functions, given by the array phi. The function is approximated using a linear combination of some coefficients multiplied by these basis functions. The approximation is done sequentially, starting from a low-level, with few basis functions, until a high-level, with many basis functions. I need to keep track of the rolling approximation of the function to determine the values of the new coefficients.
Edit 2: I've defined the functions outside the loop. However, I am struggling in working out how to create a function to keep track of the residual_function. I have attached the 'dirty' solution that works as intended for lmax=3, but I would like to generalise it for any lmax. How can I do that?
def curve_func(x):
return np.sin(x*np.pi)*x**2
def residual_func_0(x):
return curve_func(x)
# Define nodes function
def make_nodes(l):
return np.linspace(0, 1, 2**l+1, endpoint = True)
# Define new_nodes function
def make_new_nodes(l):
if (l==0):
new_nodes = np.linspace(0, 1, 2**l+1, endpoint = True)
else:
old_nodes = np.linspace(0, 1, 2**(l-1)+1, endpoint = True)
new_nodes = [x for x in make_nodes(l) if x not in old_nodes]
return new_nodes
# Define basis functions
def make_basis(l, i):
if l == 0:
return lambda x: max(1 - abs(2**l * x - i), 0)
else:
return lambda x: max(1 - abs(2**l * x - (2*i+1)), 0)
# Define coeff*basis functions
def make_scaled_basis(alpha, fn):
return lambda x: alpha * fn(x)
new_nodes_0 = make_new_nodes(0)
new_nodes_1 = make_new_nodes(1)
new_nodes_2 = make_new_nodes(2)
new_nodes_3 = make_new_nodes(3)
phi_0 = [make_basis(0, i) for i in range(len(new_nodes_0))]
phi_1 = [make_basis(1, i) for i in range(len(new_nodes_1))]
phi_2 = [make_basis(2, i) for i in range(len(new_nodes_2))]
phi_3 = [make_basis(3, i) for i in range(len(new_nodes_3))]
coeff_0 = [curve_func(n) for n in new_nodes_0]
coeff_phi_0 = [make_scaled_basis(alpha, fn) for alpha, fn in zip(coeff_0, phi_0)]
residual_func_0 = lambda x: curve_func(x) - sum(f(x) for f in coeff_phi_0)
coeff_1 = [residual_func_0(n) for n in new_nodes_1]
coeff_phi_1 = [make_scaled_basis(alpha, fn) for alpha, fn in zip(coeff_1, phi_1)]
residual_func_1 = lambda x: residual_func_0(x) - sum(f(x) for f in coeff_phi_1)
coeff_2 = [residual_func_1(n) for n in new_nodes_2]
coeff_phi_2 = [make_scaled_basis(alpha, fn) for alpha, fn in zip(coeff_2, phi_2)]
residual_func_2 = lambda x: residual_func_1(x) - sum(f(x) for f in coeff_phi_2)
coeff_3 = [residual_func_2(n) for n in new_nodes_3]
coeff_phi_3 = [make_scaled_basis(alpha, fn) for alpha, fn in zip(coeff_3, phi_3)]
residual_func_3 = lambda x: residual_func_2(x) - sum(f(x) for f in coeff_phi_3)

A simple way to localize both l and i in the body of a function is to create a function that returns a function which closes over the local variables l and i.
For example:
def make_basis(l, i):
if l == 0:
return lambda x: max(1 - abs(2**l * x - i), 0)
else:
return lambda x: max(1 - abs(2**l * x - (2*i+1)), 0)
...
for l in range(lmax):
...
phi = [make_basis(l, i) for i in range(len(new_nodes))]
Loops do not create new scopes; only function bodies do.

Related

How to output each iteration of Newton's method in Python?

I was required to have the code output each iteration and discrepancy of the Newton method for systems of nonlinear equations.
I tried and nothing worked to rearrange and correct the code so that each step of the answer was output, and not the result itself completely.
The method code itself is sure that it works optimally and outputs the correct answer.
At first I tried to enter the lower code outside the function into the inside of def, but then the problem is:
root, iter_count = Newton_method_for_systems(f_array, Jacobi_matrix, start_point, eps)
CODE:
import numpy as np
import math
def convergence_check_f(Jacobi_matrix: list, x: tuple) -> bool:
Jacobi_matrix_x = [[df_i(*x) for df_i in row] for row in Jacobi_matrix]
if np.linalg.det(Jacobi_matrix_x) != 0:
return True
return False
def Newton_method_for_systems(f_array: tuple, Jacobi_matrix: list, start_point: list, eps: float) -> list:
''' Find a root point of the system of non-linear equations using Newton's method.
Returns an approximate root and the number of iterations.'''
x_cur = np.array(start_point)
iters = 0
while True:
if not convergence_check_f(Jacobi_matrix, x_cur):
print('The condition of convergence hasn\'t been satistied: Jacobi matrix is not invertible.')
exit()
f_array_cur = np.array([f_i(*x_cur.tolist()) for f_i in f_array])
Jacobi_matrix_cur = [[df_i(*x_cur.tolist()) for df_i in row] for row in Jacobi_matrix]
# J(x^k) * delta(x^k) = -f(x^k), solve with respect to delta(x^k)
delta_x_k = np.linalg.solve(Jacobi_matrix_cur, -f_array_cur)
# delta(x^k) = x^(x+1) - x^k, so we get x^(k+1) from this equation
x_next = delta_x_k + x_cur
iters += 1
if np.linalg.norm(x_next - x_cur) < eps:
return x_next.tolist(), iters
x_cur = np.copy(x_next)
if __name__ == '__main__':
start_point = []
s1=float(input('Введите приближение x1:'))
s2=float(input('Введите приближение x2:'))
start_point.append(s1)
start_point.append(s2)
f_array = (lambda x1, x2: x1**2+x2**2-1, lambda x1, x2: math.log(x1)+2*x2+1)
Jacobi_matrix = [
[lambda x1, x2: 2*x1, lambda x1, x2: 2*x2],
[lambda x1, x2: 1/x1, lambda x1, x2: 2] ]
eps = float('Точность:')
root, iter_count = Newton_method_for_systems(f_array, Jacobi_matrix, start_point, eps)
print(f'Root: {root}, iterations: {iter_count}')

Python implementation of Matlab Code - Finite Difference Method

Given this Matlab Code created by my teacher:
function [] = explicitWave(T,L,N,J)
% Explicit method for the wave eq.
% T: Length time-interval
% L: Length x-interval
% N: Number of time-intervals
% J: Number of x-intervals
k=T/N;
h=L/J;
r=(k*k)/(h*h);
k/h
x=linspace(0,L,J+1); % number of points = number of intervals + 1
uOldOld=f(x); % solution two time-steps backwards. Initial condition
disp(uOldOld)
uOld=zeros(1,length(x)); % solution at previuos time-step
uNext=zeros(1,length(x));
% First time-step
for j=2:J
uOld(j)=(1-r)*f(x(j))+r/2*(f(x(j+1))+f(x(j-1)))+k*g(x(j));
end
% Remaining time-steps
for n=0:N-1
for j=2:J
uNext(j)=2*(1-r)*uOld(j)+r*(uOld(j+1)+uOld(j-1))-uOldOld(j);
end
uOldOld=uOld;
uOld=uNext;
end
plot(x,uNext,'r')
end
I tried to implement this in Python by using this code:
import numpy as np
import matplotlib.pyplot as plt
def explicit_wave(f, g, T, L, N, J):
"""
:param T: Length of Time Interval
:param L: Length of X-interval
:param N: Number of time intervals
:param J: Number of X-intervals
:return:
"""
k = T/N
h = L/J
r = (k**2) / (h**2)
x = np.linspace(0, L, J+1)
Uoldold = f(x)
Uold = np.zeros(len(x))
Unext = np.zeros(len(x))
for j in range(1, J):
Uold[j] = (1-r)*f(x[j]) + (r/2)*(f(x[j+1]) + f(x[j-1])) + k*g(x[j])
for n in range(N-1):
for j in range(1, J):
Unext[j] = 2*(1-r) * Uold[j]+r*(Uold[j+1]+Uold[j-1]) - Uoldold[j]
Uoldold = Uold
Uold = Unext
plt.plot(x, Unext)
plt.show()
return Unext, x
However when I run the code with the same inputs, I get different results when plotting them. My inputs:
g = lambda x: -np.sin(2*np.pi*x)
f = lambda x: 2*np.sin(np.pi*x)
T = 8.0
L = 1.0
J = 60
N = 480
Python plot result compared to exact result. The x-es represent the actual solution, and the red line is the function:
Matlab plot result , x-es represent the exact solution and the red line is the function:
Could you see any obvious errors I might have made when translating this code?
In case anyone needs the exact solution:
exact = lambda x,t: 2*np.sin(np.pi*x)*np.cos(np.pi*t) - (1/(2*np.pi))*np.sin(2*np.pi*x)*np.sin(2*np.pi*t)
I found the error through debugging. The main problem here is the code:
Uoldold = Uold
Uold = Unext
So in Python when you define a new variable as equal to an older variable, they become references to each other (i.e dependent on each other). Let me illustrate this as an example consisting of lists:
a = [1,2,3,4]
b = a
b[1] = 10
print(a)
>> [1, 10, 3, 4]
So the solution here was to use .copy()
Resulting in this:
Uoldold = Uold.copy()
Uold = Unext.copy()

How can I test linearity (superposition) & shift-invariance in Python?

I'm new to Python & writing a program that takes a function:
X = np.linspace(0,50)
F1 = np.sin(X)
Tests whether the function is linear (as in, exhibits superposition):
for i in range(1,10):
LT1 = i*(F1)
X = i*X
LT2 = F1
if np.all(LT1) == np.all(LT2):
Linear = 'This function is linear.'
elif np.all(LT1) != np.all(LT2):
Linear = 'This function is nonlinear.'
break
And tests whether the function is shift-invariant:
for j in range(1,10):
SI1 = (F1)-j
X = X-j
SI2 = F1
if np.all(SI1) == np.all(SI2):
SI = 'This function is shift-invariant.'
elif np.all(SI1) != np.all(SI2):
SI = 'This function is shift-variant.'
break
But my code calls all functions, LSI or not, linear & shift-variant. Is there a better way to run these tests? I've seen linear regression offered as a means of testing linearity, but upon trying it with a sine function it misclassified the function as nonlinear. I have also been unable to find any guidance whatsoever on testing shift-invariance.
Lets first define all the required functionality
import numpy as np
def check_linearity(T, X, a, b):
# T[a*x+b*x] = a*T[x] + b*T[x]
LHS = T(a*x + b*x)
RHS = a*T(x) + b*T(x)
tolerence = 1e-4
if np.sum(LHS-RHS) < tolerence:
print('Linear System')
return True
else:
print('Not a Linear System')
return False
def check_shift_invariance(T, X, tau):
# T[X] = T[X-tau]
LHS = T(X)
RHS = T(X-tau)
tolerence = 1e-4
if np.sum(LHS-RHS) < tolerence:
print('Shift Invariant System')
return True
else:
print('Not a Shift Invariant System')
return False
def check_LSI(T, X, a, b, tau):
flag1 = check_linearity(T, X, a, b)
flag2 = check_shift_invariance(T, X, tau)
if flag1== True and flag2==True:
print('LSI system')
else:
print('not a LSI system')
Next, we define signal
# Signal X in range [-1,1]
X = np.linspace(-1,1,10)
The, define System
# Transformation T
T = lambda x: np.sin(x)
Now lets see everything we defined in action
a = 1
b = 1
tau = 2*np.pi
# Check Linearity
check_linearity(T, X, a, b);
# Check Shift Invariance
check_shift_invariance(T, X, tau);
# Check LSI or not
check_LSI(T, X, a, b, tau);
You can easily define other systems like,
T = lambda x: x
T = lambda x: np.sin(x) + np.cos(x)
T = lambda x: x**2 + x + 2
and so on
#Ragnar provided a very nice mathematical solution, but I want to post a short one.
If your function is given by the values which are equispaced, then
print(all(abs(x) < 0.001 for x in np.diff(function, n=2)))
returns True if the function is linear and False otherwise.
The idea is that each time, the function is incremented by the same value (here I use that X is equispaced). Therefore, taking the difference of consecutive numbers should return the array with all identical entries. Taking the difference of the consecutive numbers again returns all zeros if the function is linear.

Writing lambda in MATLAB

I am trying to convert this code into MATLAB but I am not sure how to do the subscripts (Y[i] = Y[i-1]) as well as the func and f_exact variables
heres the code:
def Forward_Euler(y0,t0,T,dt,f):
t = np.arange(t0,T+dt,dt)
Y = np.zeros(len(t))
Y[0] = y0
for i in range(1,len(t)):
Y[i] = Y[i-1]+dt*f(Y[i-1], t[i-1])
return Y, t
func = lambda y,t: y-t
f_exact = lambda t: t+1-1/2*np.exp(t)
You can use anonymous functions in matlab:
func = #(y,t)(y - t)
f_exact = #(t)(t + 1 - exp(t)/2) % it works with any matrix t as well
And you can use for matrices as well (they should keep matrix operation rules). For example, in func function, as there is a minus in the form of function, the dimension of y and t must be the same.

ValueError while using scipy.integrate to calculate value of a function

I am trying to evaluate a function for different values of a variable n, I created a np.linspace for this vairiable and plugged it into a function Q:
def Q(z_i, z_min,f_gamma, mDM, sigma_v,n, with_ucmh):
dQdz_z = lambda z : dQdz(f_gamma, mDM, sigma_v, z,n, with_ucmh)
integrand = lambda z: ((1/((1+z)*H_z(z)))* (dQdz_z(z)) * np.exp(-tau(z)))
second_part = const.C*const.B*integrate.romberg(integrand,z_min,z_i)
return second_part
z_i = 2.0e6
z_min = 5.0e4
sigma_v = 3.0e-27
f_gamma = 1.
mDM = 10.
with_ucmh = True
n = np.linspace(1.,1.3,100)
mu = Q(z_i, z_min,f_gamma, mDM, sigma_v,n, with_ucmh)
I get ValueError: setting an array element with a sequence that point me to the line where I use the integrate.romberg method of scipy, but I don't understand how come I get here this sort of error..

Categories

Resources