Difference in numpy and sympy lambdify results for basic optimization - python

I wrote following code for basic optimization based on Newton's method by writing derivatives explicitly and by calculating them using sympy. Why are the results different?
Writing derivatives explicitly:
import numpy as np
def g(x):
return 1.95 - np.exp(-2/x) - 2*np.exp(-np.power(x,4))
# gd: Derivative of g
def gd(x):
return -2*np.power(x,-2)*np.exp(-2/x) + 8*np.power(x,3)*np.exp(-np.power(x,4))
def gdd(x):
return -4*np.power(x,-3)*np.exp(-2/x)-4*np.power(x,-4)*np.exp(-2/x)+24*np.power(x,2)*np.exp(-np.power(x,4))-32*np.power(x,6)*np.exp(-np.power(x,4))
# Newton's
def newton_update(x0,g,gd):
return x0 - g(x0)/gd(x0)
# Main func
x0 = 1.00
condition = True
loops = 1
max_iter = 20
while condition and loops<max_iter:
x1 = newton_update(x0,gd,gdd)
loops += 1
condition = np.abs(x0-x1) >= 0.001
x0 = x1
print('x =',x0)
if loops == max_iter:
print('Solution failed to converge. Try another starting value!')
Output:
x = 1.66382322329
x = 1.38056881356
x = 1.43948432592
x = 1.46207570893
x = 1.46847791968
x = 1.46995571549
x = 1.47027303095
Using sympy and lambdify:
import sympy as sp
x = sp.symbols('x',real=True)
f_expr = 1.95 - exp(-2/x) - 2*exp(-x**4)
dfdx_expr = sp.diff(f_expr, x)
ddfdx_expr = sp.diff(dfdx_expr, x)
# lambidify
f = sp.lambdify([x],f_expr,"numpy")
dfdx = sp.lambdify([x], dfdx_expr,"numpy")
ddfdx = sp.lambdify([x], ddfdx_expr,"numpy")
# Newton's
x0 = 1.0
condition = True
loops = 1
max_iter = 20
while condition and loops<max_iter:
x1 = newton_update(x0,dfdx,ddfdx)
loops += 1
condition = np.abs(x0-x1) >= 0.001
x0 = x1
print('x =',x0)
if loops == max_iter:
print('Solution failed to converge. Try another starting value!')
Output:
x = 1.90803013971
x = 3.96640484492
x = 6.6181614689
x = 10.5162392894
x = 16.3269006983
x = 25.0229734288
x = 38.0552735534
x = 57.5964036862
x = 86.9034400129
x = 130.860980508
x = 196.795321033
x = 295.695535237
x = 444.044999522
x = 666.568627836
x = 1000.35369299
x = 1501.03103981
x = 2252.04689304
x = 3378.57056168
x = 5068.35599056
Solution failed to converge. Try another starting value!
I made it to work by step halving in newton_update function whenever sign of derivative changes in the update. But I couldn't figure out why the results were so different with the same starting point. Also is it possible to get the same results from both?

There is a mistake in the second derivative formula in the function gdd. changing
def gdd(x):
return -4*np.power(x,-3)*np.exp(-2/x)-4*np.power(x,-4)*np.exp(-2/x)+24*np.power(x,2)*np.exp(-np.power(x,4))-32*np.power(x,6)*np.exp(-np.power(x,4))
to
def gdd(x):
return 4*np.power(x,-3)*np.exp(-2/x)-4*np.power(x,-4)*np.exp(-2/x)+24*np.power(x,2)*np.exp(-np.power(x,4))-32*np.power(x,6)*np.exp(-np.power(x,4))
should resolve the issue and produce the same result in both cases, which will be
x = 1.90803013971
x = 3.96640484492
x = 6.6181614689
x = 10.5162392894
x = 16.3269006983
x = 25.0229734288
x = 38.0552735534
x = 57.5964036862
x = 86.9034400129
x = 130.860980508
x = 196.795321033
x = 295.695535237
x = 444.044999522
x = 666.568627836
x = 1000.35369299
x = 1501.03103981
x = 2252.04689304
x = 3378.57056168
x = 5068.35599056
Solution failed to converge. Try another starting value!
This points to a problem with choosing the step size as per the comment

Related

How can i draw a quadratic function?

I have a questions concerning numpy.
i listed my data from csv package and used sklearn to plot the data.
i compiled, then plotted graph was weird.
i want to graph as "y = a * x**2 + b * x + c", gentle curve.
How can i change my code to have quadratic function?
data_list = pd.read_csv(r\'과제3_data_list.csv')
type_list = list(["B2V","B3V","B5V","B8V","B9V","A0V","A2V","A3V","A7V","F0V","F3V","F7V","G0V","G8V","K0V","K2V","K3V","K4V","K5V","K6V"])
def N(x):
y = list()
for i in range(len(x)):
if np.isnan(x[i]) == False:
y.insert(i,x[i])
return y
Type = np.array(type_list).reshape(-1,1)
standard = np.array(N(np.array(data_list["NO"]))).reshape(-1,1)
standard_mb = np.array(N(np.array(data_list["mb"]))).reshape(-1,1)
standard_size = np.array(N(np.array(data_list["mean"]))).reshape(-1,1)
star_number = np.array(N(np.array(data_list["number"]))).reshape(-1,1)
star_mbmv = np.array(N(np.array(data_list["mb-mv"]))).reshape(-1,1)
star_mv = np.array(N(np.array(data_list["mv"]))).reshape(-1,1)
evas_mbmv = np.array(N(np.array(data_list["evas_mb-mv"]))).reshape(-1,1)
evas_Mv = np.array(N(np.array(data_list["evas_Mv"]))).reshape(-1,1)
Linear = LinearRegression()
standard_size_sorted = sorted(standard_size)
Poly = PolynomialFeatures(degree=2,include_bias=False)
x_poly = Poly.fit_transform(standard_size)
Linear.fit(x_poly,standard_mb)
y = Linear.predict(x_poly)
def Poly_func(x):
y = Linear.coef_[0][0] * x + Linear.coef_[0][1] * x **2 + Linear.intercept_
return y
plt.scatter(standard_size,standard_mb) # this is just x, y value
plt.plot(standard_size,Poly_func(standard_size)) # this plot have a trouble.
plt.show()

Python2 convert function output list to callable array

I have a program I've been working on all weekend and it contains the correct output, however it outputs a list that is not callable, and I need to be able to build a plot from it. How do I modify the list to make it callable?
Homework #3 - Convolution
import numpy as np
#import matplotlib.patches as mpatches
import matplotlib.pyplot as plt
np.set_printoptions(threshold=np.nan)
#04-01 Convolution
def conv395(x,h):
#Function to convolve x input signal and h impulse response
M = len(h)-1
L = len(x)
Ly = M+L
LOL = []
def ConV(x,h,n):
y = (h[m]*x[(n-1)-m])
return (y)
for n in range(0,Ly+1):
mx = n-L+1
if mx < 1:
n = n+1
#print("SPACE")
Y1 = 0
if n <= M+1:
L1 = []
for m in range(n):
#print(n)
y = ConV(x,h,n)
Y1 = y + Y1
L1.append(Y1)
LOL.append(str(L1))
#print Y,
###LOL = np.int32(LOL)
if n > M+1:
#print("Space")
L2 = []
N = n-1
n = M+1
Y2 = 0
for m in range(n):
y2 = h[m]*x[(N-m)]
Y2 = Y2 + y2
L2.append(Y2)
LOL.append(str(L2))
if mx > 1:
#print("space")
L3 = []
if n > M+1:
Y3 = 0
for m in range(n-L,M+1):
y3 = h[m]*x[m-(n-L)]
Y3 = Y3 + y3
L3.append(Y3)
LOL.append(str(L3))
return(LOL)
h = [1,2,-1,1]
x = [1,1,2,1,2,2,1,1]
Y = conv395(x,h)
print(callable(Y))
The OUTPUT shows that the list is not callable, and I the output looks like this ['[1]', '[3]', '[3]', '[5]', '[3]', '[7]', '[4]', '[3]', '[3]', '[0]', '[1]']
I've tried every form of conversion I could find on the internet and it won't budge. Suggestions?

What is the cause of the artifacts of this convoluted signal?

I am trying to find out the cause of the artifacts that appear after convolution, they are to be seen in the plot arround x = -.0016 and x= .0021 (please see the code below). I am convoluting the "lorentzian" function (or the derivative of the langevin function) which I define in the code, with 2 Dirac impulses in the function "ditrib".
I would appreciate your help.
Thank you
Here is my code:
import numpy as np
import matplotlib.pyplot as plt
def Lorentzian(xx):
if not hasattr(xx, '__iter__'):
xx = [ xx ]
res = np.zeros(len(xx))
for i in range(len(xx)):
x = xx[i]
if np.fabs(x) < 0.1:
res[i] = 1./3. - x**2/15. + 2.* x**4 / 189. - x**6/675. + 2.* x**8 / 10395. - 1382. * x**10 / 58046625. + 4. * x**12 / 1403325.
else:
res[i] = (1./x**2 - 1./np.sinh(x)**2)
return res
amp = 18e-3
a = 1/.61e3
b = 5.5
t_min = 0
dt = 1/5e6
t_max = (10772) * dt
t = np.arange(t_min,t_max,dt)
x_min = -amp/b
x_max = amp/b
dx = dt*(x_min-x_max)/(t_min-t_max)
x = np.arange(x_min,x_max,dx)
func1 = lambda x : Lorentzian(b*(x/a))
def distrib(x):
res = np.zeros(np.size(x))
res[int(np.floor(np.size(x)/3))] = 1
res[int(3*np.floor(np.size(x)/4))] = 3
return res
func2 = lambda x,xs : np.convolve(distrib(x), func1(xs), 'same')
plt.plot(x, func2(x,x))
plt.xlabel('x (m)')
plt.ylabel('normalized signal')
try removing the "pedestal" of func1
func1(x)[0], func1(x)[-1]
Out[7]: (0.0082945964013920719, 0.008297677313152443)
just subtract
func2 = lambda x,xs : np.convolve(distrib(x), func1(xs)-func1(x)[0], 'same')
gives a smooth convolution curve
depending on the result you want you may have to add it back in after, weighted by the Dirac sum

How to calculate a Normal Distribution percent point function in python

How do I do the equivalent of scipy.stats.norm.ppf without using Scipy. I have python's Math module has erf built in but I cannot seem to recreate the function.
PS: I cannot just use scipy because Heroku does not allow you to install it and using alternate buildpacks breaches the 300Mb maximum slug size limit.
There's not a simple way to use erf to implement norm.ppf because norm.ppf is related to the inverse of erf. Instead, here's a pure Python implementation of the code from scipy. You should find that the function ndtri returns exactly the same value as norm.ppf:
import math
s2pi = 2.50662827463100050242E0
P0 = [
-5.99633501014107895267E1,
9.80010754185999661536E1,
-5.66762857469070293439E1,
1.39312609387279679503E1,
-1.23916583867381258016E0,
]
Q0 = [
1,
1.95448858338141759834E0,
4.67627912898881538453E0,
8.63602421390890590575E1,
-2.25462687854119370527E2,
2.00260212380060660359E2,
-8.20372256168333339912E1,
1.59056225126211695515E1,
-1.18331621121330003142E0,
]
P1 = [
4.05544892305962419923E0,
3.15251094599893866154E1,
5.71628192246421288162E1,
4.40805073893200834700E1,
1.46849561928858024014E1,
2.18663306850790267539E0,
-1.40256079171354495875E-1,
-3.50424626827848203418E-2,
-8.57456785154685413611E-4,
]
Q1 = [
1,
1.57799883256466749731E1,
4.53907635128879210584E1,
4.13172038254672030440E1,
1.50425385692907503408E1,
2.50464946208309415979E0,
-1.42182922854787788574E-1,
-3.80806407691578277194E-2,
-9.33259480895457427372E-4,
]
P2 = [
3.23774891776946035970E0,
6.91522889068984211695E0,
3.93881025292474443415E0,
1.33303460815807542389E0,
2.01485389549179081538E-1,
1.23716634817820021358E-2,
3.01581553508235416007E-4,
2.65806974686737550832E-6,
6.23974539184983293730E-9,
]
Q2 = [
1,
6.02427039364742014255E0,
3.67983563856160859403E0,
1.37702099489081330271E0,
2.16236993594496635890E-1,
1.34204006088543189037E-2,
3.28014464682127739104E-4,
2.89247864745380683936E-6,
6.79019408009981274425E-9,
]
def ndtri(y0):
if y0 <= 0 or y0 >= 1:
raise ValueError("ndtri(x) needs 0 < x < 1")
negate = True
y = y0
if y > 1.0 - 0.13533528323661269189:
y = 1.0 - y
negate = False
if y > 0.13533528323661269189:
y = y - 0.5
y2 = y * y
x = y + y * (y2 * polevl(y2, P0) / polevl(y2, Q0))
x = x * s2pi
return x
x = math.sqrt(-2.0 * math.log(y))
x0 = x - math.log(x) / x
z = 1.0 / x
if x < 8.0:
x1 = z * polevl(z, P1) / polevl(z, Q1)
else:
x1 = z * polevl(z, P2) / polevl(z, Q2)
x = x0 - x1
if negate:
x = -x
return x
def polevl(x, coef):
accum = 0
for c in coef:
accum = x * accum + c
return accum
The function ppf is the inverse of y = (1+erf(x/sqrt(2))/2. So we need to solve this equation for x, given y between 0 and 1. Here is a code doing this by the bisection method. I imported SciPy function to illustrate that the result is the same.
from math import erf, sqrt
from scipy.stats import norm # only for comparison
y = 0.123
z = 2*y-1
a = 0
while erf(a) > z or erf(a+1) < z: # looking for initial bracket of size 1
if erf(a) > z:
a -= 1
else:
a += 1
b = a+1 # found a bracket, proceed to refine it
while b-a > 1e-15: # 1e-15 ought to be enough precision
c = (a+b)/2.0 # bisection method
if erf(c) > z:
b = c
else:
a = c
print sqrt(2)*(a+b)/2.0 # this is the answer
print norm.ppf(y) # SciPy for comparison
Left for you to do:
preliminary bound checks (y must be between 0 and 1)
scaling and shifting if other mean / variance are desired; the code is for standard normal distribution (mean 0, variance 1).

Python - Cutting an array at a designated point based on value in row

I have a 300 x 4 matrix called X created by the odeint function. In the second column are y-values and I would like to cut the matrix when the y-value dips below 0. As a first step I was attempting to create a function that would read the second column and spit out the row number where the column first dips below 0.
X = odeint(func, X0, t)
Yval = X[:,1]
def indexer():
i = 0
if Yval[i] > 0:
i = i + 1
if Yval[i] < 0:
return i
Which is not working and conceptually I know this is wrong, I just couldn't think of another way to do this. Is there a way to cut out all the rows that contain and follow the first <0 y value?
This is my entire code:
import numpy as np
import math
from scipy.integrate import odeint
g = 9.8
theta = (45 * math.pi)/180
v0 = 10.0
k = 0.3
x0 = 0
y0 = 0
vx0 = v0*math.sin(theta)
vy0 = v0*math.cos(theta)
def func(i_state,time):
f = np.zeros(4)
f[0] = i_state[2]
f[1] = i_state[3]
f[2] = -k*(f[0]**2 + f[1]**2)**(.5)*f[0]
f[3] = -g - k*(f[0]**2 + f[1]**2)**(.5)*f[1]
return f
X0 = [x0, y0, vx0, vy0]
t0 = 0
tf = 3
timestep = 0.01
nsteps = (tf - t0)/timestep
t = np.linspace(t0, tf, num = nsteps)
X = odeint(func, X0, t)
Yval = X[:,1]
def indexer():
i = 0
if Yval[i] > 0:
i = i + 1
if Yval[i] < 0:
return i
Maybe you could use the takewhile function from the itertools package:
from itertools import takewhile
first_elements = list(takewhile(lambda x: x[1] >= 0, X))
Where X is your matrix. I used x[1] in the lambda predicate to compare the numbers in the second column.
Here, first_elements will be the rows of the matrix before the first row that contains a value less than zero. You can use len(first_elements) to know what the cutoff point was.
I converted it to a list but you don't have to if you are just going to iterate through the result.
I hope this works.
You could do something like this:
newVals = []
i = 0
while( i < len(X) and X[i][1] >= 0):
newVals.append(X[i])
i += 1
This would go through X and append values to the list newVals until you either reach the end of the list (i < len(X)) or you reach your condition (X[i][1] >= 0).

Categories

Resources