python: arbitrary precision - python

I try to make this simple program to compute the derivative of a function with normal precision:
# second derivative of a function
def diff2(f, x, h=1E-6):
r = (f(x-h) - 2*f(x) + f(x+h))/float(h*h)
return r
# define the function to derivate
def g(t):
return t**(-6)
# decresing h increasing the precision of the derivative
# ROUND-OFF problems are present
for k in range(1,15):
h = 10**(-k) # 15 different value of h
d2g = diff2(g, 1, h) # compute d'' of g 15-th times in point t=1
print 'h=%.0e: %.5f' % (h, d2g)
As one can see from the printing operation I have problem when k is larger than 8 due to round off. I know I can use:
from decimal import *
But I do not know how to implement these command in my functions.
Could someone help me please?

It is worth looking into the python module mpmath, which can handle arbitrary precision. For example:
>>> from mpmath import mp
>>> mp.dps = 50
>>> print(mp.quad(lambda x: mp.exp(-x**2), [-mp.inf, mp.inf]) ** 2)
3.1415926535897932384626433832795028841971693993751
You could simply change types and let your functions work with greater precision. It is worth noting the comments and the answer by #halex however.

If you want the derivative at position x_0 and perform your calculation with floats the optimal value for h which minimizes the numerical error is sqrt(sys.float_info.epsilon)*x_0 which is approximately 1E-8 for your case where x_0=1.
For further information and a derivation of this value see the chapter How to Choose hstarting at page 4 until the end in this short script on Numerical Differentiation.

You can use the decimal module:
from decimal import Decimal
# second derivative of a function
def diff2(f, x, h=1E-6):
x, h = Decimal(x), Decimal(h)
r = (f(x - h) - 2 * f(x) + f(x + h)) / Decimal(h * h)
return r

Related

Solving ill-posed non-linear equations numerically in python/SymPy

I'm trying to get a solution by running the code below.
Python just "hangs" and won't find a numeric solution. I can use an app on my phone (Desmos) to graph the functions and find a numeric solution easily, 0.024. Does python have limitations when solving for 2 decimal places?
import sympy
x = sympy.symbols('x')
e_1 = x**-0.5
e_2 = -2*sympy.log(0.0001*3.7**-1*0.05**-1+2.51*350000**-1*x**-0.5, 10)
sol = sympy.solve(e_2 - e_1, x, 0.024)
num = float(sol[0])
print(num)
Usually, nsolve is the SymPy tool used to numerically solve an equation (or a system of equations). However, I wasn't able to use: it kept raising errors. The problem is that your function is defined on a very small region, and the zero is very close to the boundary:
So, in this case we can try numerical root finding techniques. Again, some tools might fail, but after a few tries I've found that bisect works fine:
from sympy import *
from scipy.optimize import root, brentq, bisect
x = symbols('x')
# you didn't provide the diameter, so I've computed it based on your expected solution
d = 1.56843878182221
e_1 = x**-0.5
e_2 = -2 * log(0.00013 * 7-1*d-1+2.51350000**-1*x**-0.5, 10)
# convert the symbolic expression to a numerical function
ff = lambdify(x, e_1 - e_2)
root, output = bisect(ff, 0.023, 0.025, full_output=True)
print(root)
# 0.024000000001862646
print(output)
# converged: True
# flag: 'converged'
# function_calls: 32
# iterations: 30
# root: 0.024000000001862646
The fixed point method is a great one to use for situations like this. Or at least the principles of transforming the equation into a compatible form can benefit standard solvers by providing a less ill-behaved form of the function.
You have an ill-defined equation in the form y - g(y) where y = 1/sqrt(x). So let's get the inverse of g (call it G) so we can solve G(y) - G(g(y)) = G(y) - y instead.
>>> g = e_2.subs(1/x**.5, y)
>>> d = Dummy()
>>> G = solve(g - d, y)[0].subs(d, y)
>>> nsolve(G - y, 6)
6.45497224367903
>>> solve(1/x**.5 - _, dict=True)
{x: 0.024}
The process of rearranging an equation f(x) into form x - g(x) could probably use a built-in method in SymPy, but it's not too hard to do this by replacing each x with a dummy variable, solving for it, and then replacing the dummy symbols with x again. Different g will be more favorable for finding different roots as can be seen in the example below where the purple dashed line is good for finding the root near 1 while the solid blue is better near the smaller root.
Here is a possibility for a "fixed point form" function:
def fixedpoint_Eqs(eq, x=None):
"""rearrange to give eq in form x = g(x)"""
f = eq.free_symbols
fp = []
if x is None:
assert len(f) == 1, 'must specify x in this case'
x = list(f)[0]
Xeq = eq.replace(lambda _:_ == x, lambda _:Dummy())
X = Xeq.free_symbols - f
reps = {xi: x for xi in X}
for xi in X:
try:
g = solve(Xeq, xi, dict=True)
if len(g) != 1:
raise NotImplementedError
fp.append(Eq(x, g[0][xi].xreplace(reps)))
except NotImplementedError:
pass
return fp
>>> fixedpoint_Eqs(x+exp(x)+1/x-5)
Eq(x, -1/(x + exp(x) - 5))
Eq(x, -exp(x) + 5 - 1/x)
Eq(x, log(-x + 5 - 1/x))

Find zeroes of function with simpy.optimize.bisect, with a complex function

I'm a student of mechanical engineering, and this is the first year I've met with the Python environment, or the distribution of it Anaconda.
I was given a task to find the zeroes of this function:
𝐷⋅sin(𝛼)cos(𝛼)+𝑙⋅cos(𝛼)sin(𝛼)2βˆ’π‘™β‹…cos(𝛼)βˆ’β„Žβ‹…sin(𝛼)=0
With the parameters:
D = 220mm,
h = 1040mm,
l = 1420mm,where
n = 81
is the number of equally distanced points on the function
and the function is limited to :
π›Όβˆˆ[0,2πœ‹] where 𝛼 is a np.array.
plotted function
The issue is, when I try to insert the function in bisect(fun, a, b), the error says
'numpy.ndarray' object is not callable
Can someone aid a noob programer ? Thanks.
The question is not clear, you should share your code and the title should say scipy, not simpy, if I am correct.
Apart from this, I do not get the same plot of the function, can you check if it is correct?
If you want to use the bisection method you should do something like this:
import numpy as np
from scipy.optimize import bisect
def fun(x, D, h, l):
return D * np.sin(x) * np.cos(x) + l * np.cos(x) * np.sin(x) * 2 - l * np.cos(x) - h * np.sin(x)
D = 220
h = 1040
l = 1420
print(bisect(lambda x: fun(x, D, h, l), 0, 2*np.pi))
Note that the bisection method only finds one zero, and this does not work at all because the two extremes of the function have the same sign. For this particular function, you could run the bisect in the intervals (0, pi) and (pi, 2pi) to find both zeros.

The arctangent of tangent x is not calculated precisely

I need tangent, arctangent functions for my Python program. I tried np.arctan and np.tan but observed a strange behaviour. In principle, the arctangent of tangent of some number is the number itself, but the code below doesn't give a precise value.
import numpy as np
x = 10
print(np.arctan(np.tan(x)))
When I change x to 2*x or 3*x, then the result is even more inaccurate.
I tried math.tan, math.atan but the result was the same.
Can someone please explain why it happens(which of the two functions is wrong), and under which condition one should be careful about using the arctangent and tangent functions?
three things to note:
in python (as in all programming languages i have ever looked at) trigonometric functions work with angles in radians i.e. the range [0, 2*pi) represents the 'full circle'
tan is periodic: tan(x) = tan(pi + x) (again note that this is in radians; in python tan(x) = tan(180 + x) will not be true!).
arctan returns a value in [-pi/2, pi/2).
in your example you fall back on the correct result in [-pi/2, pi/2):
import numpy as np
x = 10
print(np.arctan(np.tan(x))) # 0.575222039231
print(10 % (np.pi / 2)) # 0.5752220392306207
your function np.arctan(np.tan(x)) is equivalent to (the computationally less expensive)
def arctan_tan(x):
ret = x % np.pi
if ret > np.pi / 2:
ret -= np.pi
return ret

Approximating derivatives using python

I have attempted to solve the following problem. I tried to solve it first with a set step size h using 0.1. However I need to change this in my code and use a for loop to loop through the values 0,1,..,20. I am a little confused how to do this problem but I was hoping to get some help with fixing the code I produced so far. Thanks!
import numpy as np
from math import sin
def derivative(func , x, h ):
for h in range(20):
return (func(x+h)-func(x))/h
def f(x):
return sin(x)
print(derivative(f, pi/4))
Gives the output
0.6706029729039897
MY EDIT:
def derivative(func , x, h ):
for h in range(20):
return (func(x+h)-func(x))/h
The exercise is asking you to compute the derivative using varying precision (represented using the variable h), and compare that to the exact/real derivative of the function.
Let h = 10 ^ -j, with j varying from 0 to 20. This means h will go (discretely) from 10⁻⁰ to 10⁻²⁰. You can use a for-loop and the range(...) function for that. Then pass that to the derivative function (to which you can a third parameter for the value of h)
def derivative(func, x, h):
return (func(x + h) - func(x)) / h
Next, you need to compare that to the exact derivative. The function f(x) = sin(x) has a known (exact) derivative which is cos(x). In math notation, d(sin x)/dx = cos x. This means that for any x, cos(x) will give you the exact derivative of sin at that x.
So you need to compare the result of the derivative(...) function to the value of cos(x). This will give you the difference. You can then use the basic Python function abs(x) to get the absolute value of that difference, which will give you the absolute difference, which is the desired result. Do that for each j from 0 to 20 and store the results somewhere, in an array or a dict.
from math import sin, cos, pi
x = pi / 4
diffs = {}
for j in range(21): # range is exclusive so range(21) will stop at 20
h = 10 ** -j
deriv = derivative(sin, x, h)
exact = cos(x)
diff = abs(deriv - exact)
diffs[h] = diff
Then, you can use pyplot's loglog function to plot those results on a graph, passing as X the range(...) result and as Y the array containing the results.
import matplotlib.pyplot as plt
ordered = sorted(diffs.items())
x, y = zip(*ordered)
plt.loglog(x, y)

Solve a system of differential equations using Euler's method

I'm trying to solve a system of ordinary differential equations with Euler's method, but when I try to print velocity I get
RuntimeWarning: overflow encountered in double_scalars
and instead of printing numbers I get nan (not a number). I think the problem might be when defining the acceleration, but I don't know for sure, I would really appreciate if someone could help me.
from numpy import *
from math import pi,exp
d=0.0005*10**-6
a=[]
while d<1.0*10**-6 :
d=d*2
a.append(d)
D=array(a)
def a_particula (D,x, v, t):
cc=((1.00+((2.00*lam)/D))*(1.257+(0.400*exp((-1.10*D)/(2.00*lam)))))
return (g-((densaire*g)/densparticula)-((mu*18.0*v)/(cc*densparticula* (D**2.00))))
def euler (acel,D, x, v, tv, n=15):
nv, xv, vv = tv.size, zeros_like(tv), zeros_like(tv)
xv[0], vv[0] = x, v
for k in range(1, nv):
t, Dt = tv[k-1], (tv[k]-tv[k-1])/float(n)
for i in range(n):
a = acel(D,x, v, t)
t, v = t+Dt, v+a*Dt
x = x+v*Dt
xv[k], vv[k] = x, v
return (xv, vv)
g=(9.80)
densaire= 1.225
lam=0.70*10**-6
densparticula=1000.00
mu=(1.785*10**-5)
tv = linspace(0, 5, 50)
x, v = 0, 0 #initial conditions
for j in range(len(D)):
xx, vv = euler(a_particula, D[j], x, v, tv)
print(D[j],xx,vv)
In future it would be helpful if you included the full warning message in your question - it will contain the line where the problem occurs:
tmp/untitled.py:15: RuntimeWarning: overflow encountered in double_scalars
return (g-((densaire*g)/densparticula)-((mu*18.0*v)/(cc*densparticula* (D**2.00))))
Overflow occurs when the magnitude of a variable exceeds the largest value that can be represented. In this case, double_scalars refers to a 64 bit float, which has a maximum value of:
print(np.finfo(float).max)
# 1.79769313486e+308
So there is a scalar value in the expression:
(g-((densaire*g)/densparticula)-((mu*18.0*v)/(cc*densparticula* (D**2.00))))
that is exceeding ~1.79e308. To find out which one, you can use np.errstate to raise a FloatingPointError when this occurs, then catch it and start the Python debugger:
...
with errstate(over='raise'):
try:
ret = (g-((densaire*g)/densparticula)-((mu*18.0*v)/(cc*densparticula* (D**2.00))))
except FloatingPointError:
import pdb
pdb.set_trace()
return ret
...
From within the debugger you can then check the values of the various parts of this expression. The overflow seems to occur in:
(mu*18.0*v)/(cc*densparticula* (D**2.00))
The first time the warning occurs, (cc*densparticula* (D**2.00) evaluates as 2.3210168586496022e-12, whereas (mu*18.0*v) evaluates as -9.9984582297025182e+299.
Basically you are dividing a very large number by a very small number, and the magnitude of the result is exceeding the maximum value that can be represented. This might be a problem with your math, or it might be that your inputs to the function are not reasonably scaled.
Your system reduces to
dv/dt = a = K - L*v
with K about 10 and L ranging between, at first glance 1e+5 to 1e+10. The actual coefficients used confirm that:
D=1.0000e-09 K= 9.787995 L=1.3843070e+08
D=3.2000e-08 K= 9.787995 L=4.2570244e+06
D=1.0240e-06 K= 9.787995 L=9.0146813e+04
The Euler step for the velocity is
v[j+1]=v[j]+(K-L*v[j])*dt =(1-L*dt)*v[j] + K*dt
For anything resembling the intended friction effect, i.e., the velocity falling to K/L one needs that abs(1-L*dt)<1, and if possible 0<1-L*dt<1, that is, dt < 1/L. Which means here that dt < 1e-10.
To be able to use larger time steps you need to use methods for stiff differential equations, which means implicit methods. The most simple ones are the implicit Euler method, the midpoint method and the trapezoidal method.
Because of the linearity, the midpoint and trapezoidal method amount to the same formula
v[j+1] = v[j] + dt * ( K - L*(v[j]+v[j+1])/2 )
or
v[j+1] = ( (1-L*dt/2)*v[j] + K*dt ) / (1+L*dt/2)
Of course, the most simple method is to just exactly integrate the ODE
(-L*v')/(K-L*v)=-L => K-L*v(t)=C*exp(-L*t), C=K-L*v(0)
v(t)=K/L + exp(-L*t)*(v(0)-K/L)
which integrates to
x(t)=x(0)+K/L*t+(1-exp(-L*t))/L*(v(0)-K/L).
Alternatively it is possible that you made an error in transcribing the physical laws into formulas so that the magnitudes of the constants are all wrong.

Categories