I'm currently trying to solve the following equation for x:
3.17e-2 - integral from x to 215 of [(10.^(8.64/x) / (480.1 - 10.^(4.32/x))^2)]dx = 0.
(sorry for writing the equation in such a crude way, I wasn't sure on how to insert latex on here)
so far I've come up with this:
import scipy as s
from scipy.integrate import odeint,quad
import numpy as np
def f(x):
fpe = 40
k = 1.26e4*fpe**2/4.2e4
return 10.**(8.64/x) / (k - 10.**(4.32/x))**2
def intf(x):
for i in x:
if 3.17e-2 - quad(lambda i:f(i),i,215) == 0.:
print(i)
xi = np.linspace(0.01, 5, 1000)
intf(xi)
However, I keep getting the following error:
OverflowError: (34, 'Result too large')
As you can imagine, this is not the result I was expecting. Do you reckon that this is only due to the result being too large or could there be something wrong with the code?
One thing you have to change quad returns a tuple (y, abserr), the result of the integral is quad(...)[0]
Also, if you compare f(x) == 0 you will only detect exact solutions, that will be impossible for this function in floating point computation. You could use abs(f(x)) < ytol, or simply use a zero finding method. I would suggest you to use fsolve
Another thing is that you have the derivative of the function, so you can pass that to the fsolve as well, putting all together you have
import numpy as np
from scipy.integrate import quad
from scipy.optimize import fsolve
def fprime(x):
fpe = 40
k = 1.26e4*fpe**2/4.2e4
return 10.**(8.64/x) / (k - 10.**(4.32/x))**2
def f(x):
try:
return np.array([f(i) for i in x])
except TypeError:
return 3.17e-2 - quad(lambda i:fprime(i),x,215)[0]
from scipy.optimize import fsolve
x0 = fsolve(f, 1, fprime=fprime)
this gives x0=2.03740802, and f(x0) = 2.35922393e-16
Related
I need to calculate the error in each iteration at solving a system of equation non-linear with fsolve in python, like resnorm of fsolve in MATLAB. Someone can help me if it's possible in python?
This can be easily achieved by peeking at the docs.
import numpy as np
import matplotlib.pyplot as plt
from scipy.optimize import fsolve
# Use the function from the docs
def func(x):
return [x[0] * np.cos(x[1]) - 4,
x[1] * x[0] - x[1] - 5]
roots_dict = fsolve(func, x0 = [1,1], full_output = True)
# calculate the square sum of the residuals:
res = np.sum(roots_dict[1]['fvec'] ** 2)
The sum of the square of the residuals will be:
res
>> 2.754320890449926e-22
I would like to make an iteration in order to get a function verifying an integral equation. The integral operator is hidden in the function lambdaop. However, I just cannot iterate on the process getting an error
File "blablabla/.spyder-py3/temp.py", line 11, in integrand
return -0.5*f(x)*scipy.special.expi(-abs(x-tau))
TypeError: can't multiply sequence by non-int of type 'float'
that I do not understand. I have done here for only two functions by my ultimate goal would be to make n iteration. I guess the error comes from the way of defining the function that is not suitable (maybe not use "lambda definitions" ... ) but I do not want to create a vector of size n in place of the function since I want to integrate it after. Anyone has an idea how to fix this problem ?
import numpy as np
import matplotlib.pyplot as plt
import math
import scipy.integrate
import scipy.special
def initial(x):
return x + 2/3
def integrand(f,x,tau):
return -0.5*f(x)*scipy.special.expi(-abs(x-tau))
def lambdaop(f,x,tau):
def step(x,tau):
return integrand(f,x,tau)
g = lambda tau: scipy.integrate.quad(step,0,np.inf,args=(tau,))
return g
g = lambdaop(initial,1,2)
h = lambdaop(g,1,2)
print(h(5))
x = np.linspace(0,3,101)
y = np.linspace(0,3,101)
for i in np.arange(101):
y[i] = h(x[i])[0]
plt.plot(x,y)
plt.show()
I have not seen any other topic like mine but in case it is a duplicate, please excuse me.
Add some print statements in your integrand function.
For example,
def integrand(f,x,tau):
print('-------')
print('args:', f, x, tau)
v = f(x)
print(f'f({x})={v}')
s = scipy.special.expi(-abs(x-tau))
print(f's={s}')
result = -0.5*v*s
print('=====')
return result
and you'll see that:
f(1.0)=(inf, inf)
s=-0.0037793524098489063
The (inf, inf) is where the error message is coming from.
I'm trying to plot the output from an ODE using a Kronecker delta function which should only become 'active' at a specific time = t1.
This should give a sawtooth like response where the initial value decays down exponentially until t=t1 where it rises again instantly before decaying down once again.
However, when I plot this it looks like the solver is seeing the Kronecker delta function as zero for all time t. Is there anyway to do this in Python?
from scipy import KroneckerDelta
import scipy.integrate as sp
import matplotlib.pyplot as plt
import numpy as np
def dy_dt(y,t):
dy_dt = 500*KroneckerDelta(t,t1) - 2y
return dy_dt
t1 = 4
y0 = 500
t = np.arrange(0,10,0.1)
y = sp.odeint(dy_dt,y0,t)
plt.plot(t,y)
In the case of a simple Kronecker delta using time, you can run the ode in pieces like so:
from scipy.integrate import odeint
import matplotlib.pyplot as plt
import numpy as np
def dy_dt(y,t):
return -2*y
t_delta = 4
tend = 10
y0 = [500]
t1 = np.linspace(0,t_delta,50)
y1 = odeint(dy_dt,y0,t1)
y0 = y1[-1] + 500 # execute Kronecker delta
t2 = np.linspace(t_delta,tend,50)
y2 = odeint(dy_dt,y0,t2)
t = np.append(t1, t2)
y = np.append(y1, y2)
plt.plot(t,y)
Another option for complicated situations is to the events functionality of solve_ivp.
I think the problem could be internal rounding errors, because 0.1 cannot be represented exactly as a python float. I would try
import math
def dy_dt(y,t):
if math.isclose(t, t1):
return 500 - 2*y
else:
return -2y
Also the documentation of odeint suggests using the args parameter instead of global variables to give your derivative function access to additional arguments and replacing np.arange by np.linspace:
import scipy.integrate as sp
import matplotlib.pyplot as plt
import numpy as np
import math
def dy_dt(y, t, t1):
if math.isclose(t, t1):
return 500 - 2*y
else:
return -2*y
t1 = 4
y0 = 500
t = np.linspace(0, 10, num=101)
y = sp.odeint(dy_dt, y0, t, args=(t1,))
plt.plot(t, y)
I did not test the code so tell me if there is anything wrong with it.
EDIT:
When testing my code I took a look at the t values for which dy_dt is evaluated. I noticed that odeint does not only use the t values that where specified, but alters them slightly:
...
3.6636447422787928
3.743098503914526
3.822552265550259
3.902006027185992
3.991829287543431
4.08165254790087
4.171475808258308
...
Now using my method, we get
math.isclose(3.991829287543431, 4) # False
because the default tolerance is set to a relative error of at most 10^(-9), so the odeint function "misses" the bump of the derivative at 4. Luckily, we can fix that by specifying a higher error threshold:
def dy_dt(y, t, t1):
if math.isclose(t, t1, abs_tol=0.01):
return 500 - 2*y
else:
return -2*y
Now dy_dt is very high for all values between 3.99 and 4.01. It is possible to make this range smaller if the num argument of linspace is increased.
TL;DR
Your problem is not a problem of python but a problem of numerically solving an differential equation: You need to alter your derivative for an interval of sufficient length, otherwise the solver will likely miss the interesting spot. A kronecker delta does not work with numeric approaches to solving ODEs.
I am trying to solve this simple simultaneous equations using scipy's fsolve function:
x + 2 = 10 &
x^2 = 64.
I am expecting 8 as the solution. However I'm getting an error saying "minpack.error: Result from function call is not a proper array of floats."
I am pretty new to python scientific library. Can someone please explain how to solve this error? Thanks!
from scipy.optimize import fsolve
def equations(p):
x = p
return (x-8, x**2 - 64)
x = fsolve(equations, 1)
print(x)
When you look at how fsolve is defined in the scipy module we see:
def fsolve(func, x0, args=(), fprime=None, full_output=0,
col_deriv=0, xtol=1.49012e-8, maxfev=0, band=None,
epsfcn=None, factor=100, diag=None):
"""
Find the roots of a function.
Return the roots of the (non-linear) equations defined by
``func(x) = 0`` given a starting estimate.
Parameters
----------
func : callable ``f(x, *args)``
A function that takes at least one (possibly vector) argument,
and returns a value of the same length.
'''
So your input value for p should consist of just as many elements as are returned by your function. Try for example:
from scipy.optimize import fsolve
import numpy as np
def equations(p):
x1 = p[0]
x2 = p[1]
return x1-8, x2**2 - 64
x = fsolve(equations, np.array([1, 2]))
print(x)
which gives 8, 8 as an answer.
I don't have a lot of experience with Python but I decided to give it a try in solving the following system of equations:
x = A * exp (x+y)
y = 4 * exp (x+y)
I want to solve this system and plot x and y as a function of A.
I saw some a similar question and give fsolve a try:
`from scipy.optimize import fsolve
def f(p):
x, y = p
A = np.linspace(0,4)
eq1= x -A* np.exp(x+y)
eq2= y- 4* np.exp(x+y)
return (eq1,eq2)
x,y = fsolve(f,(0, 0))
print(x,y)
plt.plot(x,A)
plt.plot(y,A)
`
I'm getting these errors:
setting an array element with a sequence.
Result from function call is not a proper array of floats.
Pass the value of A as argument to the function and run fsolve for each value of A separately.
Following code works.
from scipy.optimize import fsolve
import matplotlib.pyplot as plt
import numpy as np
def f(p,*args):
x, y = p
A = args[0]
return (x -A* np.exp(x+y),y- 4* np.exp(x+y))
A = np.linspace(0,4,5)
X = []
Y =[]
for a in A:
x,y = fsolve(f,(0.0, 0.0) , args=(a))
X.append(x)
Y.append(y)
print(x,y)
plt.plot(A,X)
plt.plot(A,Y)
4.458297786441408e-17 -1.3860676807976662
-1.100088440495758 -0.5021704548996653
-1.0668987418054918 -0.7236105952221454
-1.0405000943788385 -0.9052366768954621
-1.0393471472966025 -1.0393471472966027
/usr/local/lib/python3.6/dist-packages/scipy/optimize/minpack.py:163: RuntimeWarning: The iteration is not making good progress, as measured by the
improvement from the last ten iterations.
warnings.warn(msg, RuntimeWarning)
/usr/local/lib/python3.6/dist-packages/scipy/optimize/minpack.py:163: RuntimeWarning: The iteration is not making good progress, as measured by the
improvement from the last five Jacobian evaluations.
warnings.warn(msg, RuntimeWarning)
[<matplotlib.lines.Line2D at 0x7f4a2a83a4e0>]