Calculate implicit integral function contours with python - python

I guess I am missing something in this code:
integrand = lambda t,x,y: (1/(Tiempo-t))*np.exp(-((x-U*(Tiempo-t))**2+y**2)/(4*a*(Tiempo-t)))
def z_func(x,y,Rate,Conductivity):
integral, err = integrate.quad(integrand,0,Tiempo,args=(x,y,))
return ((Rate/(2*math.pi* Conductivity))*integral)
Z = z_func(X, Y, Ql, k)
cs = plt.contour(X, Y, Z,[IncT])
I have an implicit function with an integral, something like f(x,y,t)=A*Integral, where A is constant. It integrates over t. I need to calculate the contour for an specific value of t. But I get several errors such as "Supplied function does not return a valid float", which is the actual error when evaluating the z_func.
What am I doing wrong? Is there another way to solve it?
I should add I'm working with a meshgrid:
x = arange(-1.0,10.0,0.1)
y = arange(-1.0,10.0,0.1)
X,Y = meshgrid(x, y)
Thanks in advance!

To avoid this error, z_func must be vectorized:
vz_func = np.vectorize(z_func)
Z = vz_func(X, Y, Ql, k)

Related

How to write a function to fit data to a sum of N Gaussian-like peaks without explicitly defining the expression for every possible N?

I am trying to fit a progression of Gaussian peaks to a spectral lineshape.
The progression is a summation of N evenly spaced Gaussian peaks. When coded as a function, the formula for N=1 looks like this:
A * ((e0-i*hf)/e0)**3 * ((S**i)/np.math.factorial(i)) * np.exp(-4*np.log(2)*((x-e0+i*hf)/fwhm)**2)
where A, e0, hf, S and fwhm are to be determined from the fit with some good initial guesses.
Importantly, the parameter i starts at 0 and is incremented by 1 for every additional component.
So, for N = 3 the expression would take the form:
A * ((e0-0*hf)/e0)**3 * ((S**0)/np.math.factorial(0)) * np.exp(-4*np.log(2)*((x-e0+0*hf)/fwhm)**2) +
A * ((e0-1*hf)/e0)**3 * ((S**1)/np.math.factorial(1)) * np.exp(-4*np.log(2)*((x-e0+1*hf)/fwhm)**2) +
A * ((e0-2*hf)/e0)**3 * ((S**2)/np.math.factorial(2)) * np.exp(-4*np.log(2)*((x-e0+2*hf)/fwhm)**2)
All the parameters except i are constant for every component in the summation, and this is intended. i is changing in a controlled way depending on the number of parameters.
I am using curve_fit. One way to code the fitting routine would be to explicitly define the expression for any reasonable N and just use an appropriate one. Like, here it'would be 5 or 6, depending on the spacing, which is determined by hf. I could just define a long function with N components, writing an appropriate i value into each component. I understand how to do that (and did). But I would like to code this more intelligently. My goal is to write a function that will accept any value of N, add the appropriate amount of components as described above, compute the expression while incrementing the i properly and return the result.
I have attempted a variety of things. My main hurdle is that I don't know how to tell the program to use a particular N and the corresponding values of i. Finally, after some searching I thought I found a good way to code it with a lambda function.
from scipy.optimize import curve_fit
import numpy as np
def fullfunc(x,p,n):
def func(x,A,e0,hf,S,fwhm,i):
return A * ((e0-i*hf)/e0)**3 * ((S**i)/np.math.factorial(i)) * np.exp(-4*np.log(2)*((x-e0+i*hf)/fwhm)**2)
y_fit = np.zeros_like(x)
for i in range(n):
y_fit += func(x,p[0],p[1],p[2],p[3],p[4],i)
return y_fit
p = [1,26000,1400,1,1000]
x = [27027,25062,23364,21881,20576,19417,18382,17452,16611,15847,15151]
y = [0.01,0.42,0.93,0.97,0.65,0.33,0.14,0.06,0.02,0.01,0.004]
n = 7
fittedParameters, pcov = curve_fit(lambda x,p: fullfunc(x,p,n), x, y, p)
A,e0,hf,S,fwhm = fittedParameters
This gives:
TypeError: <lambda>() takes 2 positional arguments but 7 were given
and I don't understand why. I have a feeling the lambda function can't deal with a list of initial parameters.
I would greatly appreciate any advice on how to make this work without explicitly writing all the equations out, as I find that a bit too rigid.
The x and y ranges provided are samples of real data which give a general idea of what the shape is.
Since you only use summation over a range i=0, 1, ..., n-1, there is no need to refer to complicated lambda constructs that may or may not work in the context of curve fit. Just define your fit function as the summation of n components:
from matplotlib import pyplot as plt
from scipy.optimize import curve_fit
import numpy as np
def func(x, A, e0, hf, S, fwhm):
return sum((A * ((e0-i*hf)/e0)**3 * ((S**i)/np.math.factorial(i)) * np.exp(-4*np.log(2)*((x-e0+i*hf)/fwhm)**2)) for i in range(n))
p = [1,26000,1400,1,1000]
x = [27027,25062,23364,21881,20576,19417,18382,17452,16611,15847,15151]
y = [0.01,0.42,0.93,0.97,0.65,0.33,0.14,0.06,0.02,0.01,0.004]
n = 7
fittedParameters, pcov = curve_fit(func, x, y, p0=p)
#A,e0,hf,S,fwhm = fittedParameters
print(fittedParameters)
plt.plot(x, y, "ro", label="data")
x_fit = np.linspace(min(x), max(x), 100)
y_fit = func(x_fit, *fittedParameters)
plt.plot(x_fit, y_fit, label="fit")
plt.legend()
plt.show()
Sample output:
P.S.: By the look of it, these data points are already well fitted with n=1.

How do I get SymPy to collect partial derivatives?

I have been using SymPy to expand the terms of a complex partial differential equation and would like to use the collect function to gather terms. However, it seems to have a problem dealing with second (or higher order) derivatives where the variables of differentiation differ.
In the code example below collect(expr6... works, but collect(expr7 ... does not, returning the error message "NotImplementedError: Improve MV Derivative support in collect". The error is clearly related to the psi.diff(x,y) difference in the two cases. Is it obvious to anyone what I need to do to have collect(expr7 ... work?
cheers
Richard
Example:
from sympy import *
psi = Function("psi") (x,y,z,t)
expr6=2*psi.diff(x,x)+3*U*psi.diff(x)+5*psi.diff(y)
expr7=2*psi.diff(x,y)+3*U*psi.diff(x)+5*psi.diff(y)
collect(expr6, psi.diff(x),evaluate=False, exact=False) # works
#collect(expr7, psi.diff(x),evaluate=False, exact=False)
# throws an error: NotImplementedError: Improve MV Derivative support in collect
I've bumped into this issue and my workaround is to perform a substitution with simple dummy variables first, collect based on these simple variables, and then substitute back the more advanced variables. There might be some corner cases, but it seems to work for me.
from sympy import symarray, collect
def mycollect(expr, var_list, evaluate=True, **kwargs):
""" Acts as collect but substitute the symbols with dummy symbols first so that it can work with partial derivatives.
Matrix expressions are also supported.
"""
if not hasattr(var_list, '__len__'):
var_list=[var_list]
# Mapping Var -> Dummy, and Dummy-> Var
Dummies=symarray('DUM', len(var_list))
Var2Dummy=[(var, Dummies[i]) for i,var in enumerate(var_list)]
Dummy2Var=[(b,a) for a,b in Var2Dummy]
# Replace var with dummies and apply collect
expr = expr.expand().doit()
expr = expr.subs(Var2Dummy)
if hasattr(expr, '__len__'):
expr = expr.applyfunc(lambda ij: collect(ij, Dummies, **kwargs))
else:
expr = collect(expr, Dummies, evaluate=evaluate, **kwargs)
# Substitute back
if evaluate:
return expr.subs(Dummy2Var)
d={}
for k,v in expr.items():
k=k.subs(Dummy2Var)
v=v.subs(Dummy2Var)
d[k]=v
return d
For your example:
mycollect(expr6, psi.diff(x), evaluate=False)
mycollect(expr7, psi.diff(x), evaluate=False)
returns:
{Derivative(psi(x, y, z, t), (x, 2)): 2, Derivative(psi(x, y, z, t), x): 3*U, 1: 5*Derivative(psi(x, y, z, t), y)}
{Derivative(psi(x, y, z, t), x, y): 2, Derivative(psi(x, y, z, t), x): 3*U, 1: 5*Derivative(psi(x, y, z, t), y)}

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)

solving differential equation with step function

I am trying to solve this differential equation as part of my assignment. I am not able to understand on how can i put the condition for u in the code. In the code shown below, i arbitrarily provided
u = 5.
2dx(t)dt=−x(t)+u(t)
5dy(t)dt=−y(t)+x(t)
u=2S(t−5)
x(0)=0
y(0)=0
where S(t−5) is a step function that changes from zero to one at t=5. When it is multiplied by two, it changes from zero to two at that same time, t=5.
def model(x,t,u):
dxdt = (-x+u)/2
return dxdt
def model2(y,x,t):
dydt = -(y+x)/5
return dydt
x0 = 0
y0 = 0
u = 5
t = np.linspace(0,40)
x = odeint(model,x0,t,args=(u,))
y = odeint(model2,y0,t,args=(u,))
plt.plot(t,x,'r-')
plt.plot(t,y,'b*')
plt.show()
I do not know the SciPy Library very well, but regarding the example in the documentation I would try something like this:
def model(x, t, K, PT)
"""
The model consists of the state x in R^2, the time in R and the two
parameters K and PT regarding the input u as step function, where K
is the infimum of u and PT is the delay of the step.
"""
x1, x2 = x # Split the state into two variables
u = K if t>=PT else 0 # This is the system input
# Here comes the differential equation in vectorized form
dx = [(-x1 + u)/2,
(-x2 + x1)/5]
return dx
x0 = [0, 0]
K = 2
PT = 5
t = np.linspace(0,40)
x = odeint(model, x0, t, args=(K, PT))
plt.plot(t, x[:, 0], 'r-')
plt.plot(t, x[:, 1], 'b*')
plt.show()
You have a couple of issues here, and the step function is only a small part of it. You can define a step function with a simple lambda and then simply capture it from the outer scope without even passing it to your function. Because sometimes that won't be the case, we'll be explicit and pass it.
Your next problem is the order of arguments in the function to integrate. As per the docs (y,t,...). Ie, First the function, then the time vector, then the other args arguments. So for the first part we get:
u = lambda t : 2 if t>5 else 0
def model(x,t,u):
dxdt = (-x+u(t))/2
return dxdt
x0 = 0
y0 = 0
t = np.linspace(0,40)
x = odeint(model,x0,t,args=(u,))
Moving to the next part, the trouble is, you can't feed x as an arg to y because it's a vector of values for x(t) for particular times and so y+x doesn't make sense in the function as you wrote it. You can follow your intuition from math class if you pass an x function instead of the x values. Doing so requires that you interpolate the x values using the specific time values you are interested in (which scipy can handle, no problem):
from scipy.interpolate import interp1d
xfunc = interp1d(t.flatten(),x.flatten(),fill_value="extrapolate")
#flatten cuz the shape is off , extrapolate because odeint will go out of bounds
def model2(y,t,x):
dydt = -(y+x(t))/5
return dydt
y = odeint(model2,y0,t,args=(xfunc,))
Then you get:
#Sven's answer is more idiomatic for vector programming like scipy/numpy. But I hope my answer provides a clearer path from what you know already to a working solution.

partial integration of a two dimensional gaussian function

I want to carry out the following partial integration of a 2-D gaussian function of four variables (x, y, alpha and beta), with respect to only x and y, as follows. In the end I want the answer to be a function of alpha and beta only.
I wrote the following code in python to execute the above mentioned integral.
from sympy import Symbol
from sympy import integrate
from math import e
alpha = Symbol('alpha')
beta = Symbol('beta')
x = Symbol('x')
y = Symbol('y')
n = 2
value = integrate( e**( -(x - alpha)**n - (y - beta)**n ), (x, -1, 1), (y, -1, 1) )
However I get the following error:
sympy.polys.polyerrors.DomainError: there is no ring associated with RR
The above mentioned integrate function works fine for n=1. However it breaks down for n>1.
Am I doing something wrong?
Welcome to SO!
Interestingly it works when you substitute alpha and beta into the integral bounds. Try:
from IPython.display import display
import sympy as sy
sy.init_printing() # LaTeX like pretty printing forIPython
alpha, beta, x, y = sy.symbols("alpha, beta, x, y", real=True)
f = sy.exp(-x**2 - y**2) # sy.exp() is better than the numeric constant
val = sy.integrate(f, (x, -1+alpha, 1+alpha), (y, -1+beta, 1+beta))
display(val)

Categories