How to simply integral a function written in Sympy - python

Through using the ways and obtaining help from Stackoverflow users, I could find half of the solution and I need to complete it.
Through using Sympy I could produce my function parametrically and it became 100 different items similar to 0.03149536*exp(-4.56*s)*sin(2.33*s) 0.03446408*exp(-4.56*s)*sin(2.33*s). By using f = lambdify(s,f) I converted it to a NumPy function and I needed to do integral of in the different sthat I already have. The upper limit of the integral is a constant value and the lower limit must be done through afor loop`.
When I try to do, I get some error which I post below. The code that I wrote is below, but for being a reproducible question I have to put a generated data. TypeError: cannot determine truth value of Relational
from sympy import exp, sin, symbols, integrate, lambdify
import pandas as pd
import matplotlib.pyplot as plt
from scipy import integrate
import numpy as np
S = np.linspace(0,1000,100)
C = np.linspace(0,1,100)
s, t = symbols('s t')
lanr = -4.56
lani = -2.33
ID = S[-1]
result=[]
f = C * exp(lanr * s) * sin (lani * s)
f = lambdify(s,f)
#vff = np.vectorize(f)
for i in (S):
I = integrate.quad(f,s,(i,ID))
result.append(I)
print(result)
EDIT
I tried to do the same without havingSympy by using just Scipy and wrote the code below and again I could not solve the problem.
from scipy.integrate import quad
import numpy as np
lanr = -6.55
lani = -4.22
def integrand(c,s):
return c * np.exp(lanr * s) * np.sin (lani * s)
def self_integrate(c,s):
return quad(integrand,s,1003,1200)
import pandas as pd
file = pd.read_csv('1-100.csv',sep="\s+",header=None)
s = np.linspace(0,1000,100)
c = np.linspace(0,1,100)
ID = s[-1]
for i in s:
I = self_integrate(integrand,c,s)
print(I)
and I got this TypeError: self_integrate() takes 2 positional arguments but 3 were given

Assuming you want to integrate over s, and use c as a fixed parameter (for a given quad call), define:
In [198]: lanr = 1
...: lani = 2
...: def integrand(s, c):
...: return c * np.exp(lanr * s) * np.sin (lani * s)
...:
test it by itself:
In [199]: integrand(10,1.23)
Out[199]: 24734.0175253505
and test it in quad:
In [200]: quad(integrand, 0, 10, args=(1.23,))
Out[200]: (524.9015616747192, 3.381048596651226e-08)
doing the same for a range of c values:
In [201]: alist = []
...: for c in range(0,10):
...: x,y = quad(integrand, 0, 10, args=(c,))
...: alist.append(x)
...:
In [202]: alist
Out[202]:
[0.0,
426.74923713391905,
853.4984742678381,
1280.2477114017531,
1706.9969485356762,
2133.7461856695877,
2560.4954228035062,
2987.244659937424,
3413.9938970713524,
3840.743134205258]
From the quad docs:
quad(func, a, b, args=(),...)
func : {function, scipy.LowLevelCallable}
A Python function or method to integrate. If `func` takes many
arguments, it is integrated along the axis corresponding to the
first argument.
and an example:
>>> f = lambda x,a : a*x
>>> y, err = integrate.quad(f, 0, 1, args=(1,))
The docs are a bit long, but the basics should be straight forward.
I was tempted to say you were stuck on the sympy calling pattern, but the second argument for that is either the integration symbol, or a tuple.
>>> integrate(log(x), (x, 1, a))
a*log(a) - a + 1
So I'm puzzled as to why you were stuck on using
quad(integrand,s,1003,1200)
The s, whether a sympy variable or a linspace array does not make sense.

Related

Problem in writing an itegration in python

I have 2 columns of arrays. I also have an equation(integration) that uses 2 columns. I need to have the integral values, using two columns values. In the sense that, for each s there is c.
add the third column which would be the integration result based on a specific index number as an upper and under the limit.
As an example, take a look at the values below:
ID=50
s = np.arange(0,100)
c = np.arange(200,300)
lanr=-4.56
lani=-2.33
and the integration that I need to be solved is c(s) * exp(lanr * s) * sin (lani * s). Now my problem is adding the third column with the result of the integration between 0,s[ID], which means that and I need to have the integral with detail that I mentioned in the question between s=0 to s=ID.
I have written something below which does not work:
from scipy.integrate import quad
import numpy as np
from sympy import *
def f(s):
return c*(s) * exp(lanr * s) * sin (lani * s)
integ = []
for i in enumerate(s):
g = c * np.exp(lanr * s) * np.sin(lani * s)
integrate( f(s), s,0,ID)
Maybe the following is similar to what you're looking for?
At the start, we can try to only work symbolically. s is the basic variable. c is a function of s, in this case writing c = s + 200 makes c such a function.
f=c*exp(lanr*s)*sin(lani*s) is a more complicated function of s. print(f) gives -(s+200)*exp(-4.56*s)*sin(2.33*s).
Now, you seem to be interested in the integral of f for s going from 0 till some value. Let's call that value t. Then, that integral would be a function g of t.
from sympy import exp, sin, symbols, integrate, lambdify
s, t = symbols('s t')
lanr = -4.56
lani = -2.33
c = s + 200
f = c * exp(lanr * s) * sin (lani * s)
g = integrate(f, (s, 0, t))
If only 101 values are needed, we can stay inside sympy:
values = [g.subs(t, ti).evalf() for ti in range(0, 101)]
If more numeric calculations are needed, lambdify() can convert g from sympy to numpy.
Then numpy can also calculate the first 101 values (this works much faster than in sympy, but that is only important if many more calculations are needed):
g_np = lambdify(t, g)
import numpy as np
x = np.arange(0,100)
y = g_np(x)
In this case the result would be
array([ 0. , -17.66531171, -17.80584637, -17.80185932,
-17.8019015 , -17.80190133, -17.80190133, -17.80190133,
-17.80190133, -17.80190133, -17.80190133, -17.80190133,
-17.80190133, -17.80190133, -17.80190133, -17.80190133,
...
This looks quite strange. Maybe there is some misunderstanding somewhere? Or maybe the original formula has some mistake?

Error using scicpy.integrate.odeint and sympy symbols

I'm trying to solve the following system: d²i/dt² + R'(i)/L di/dt + 1/LC i(t) = 1/L dE/dt as a set of coupled first order differential equations:
di/dt = k
dk/dt = 1/L dE/dt - R'(i)/L k - 1/LC i(t)
Here is the code I'm using:
import numpy as np
import sympy as sp
import matplotlib.pyplot as plt
from scipy.integrate import odeint
#Define model: x = [i , k]
def RLC(x , t):
i = sp.Symbol('i')
t = sp.Symbol('t')
#Data:
E = sp.ln(t + 1)
dE_dt = E.diff(t)
R1 = 1000 #1 kOhm
R2 = 100 #100 Ohm
R = R1 * i + R2 * i**3
dR_di = R.diff(i)
i = x[0]
k = x[1]
L = 10e-3 #10 mHy
C = 1.56e-6 #1.56 uF
#Model
di_dt = k
dk_dt = 1/L * dE_dt - dR_di/L * k - 1/(L*C) * i
dx_dt = np.array([di_dt , dk_dt])
return dx_dt
#init cond:
x0 = np.array([0 , 0])
#time points:
time = np.linspace(0, 30, 1000)
#solve ODE:
x = odeint(RLC, x0, time)
i = x[: , 0]
However, I get the following error: TypeError: Cannot cast array data from dtype('O') to dtype('float64') according to the rule 'safe'
So, I don't know if sympy and odeint don't work well together. Or maybe is it a problem because I defined t as sp.Symbol?
When you differentiate a function, you get a function back. So you need to evaluate it at a point in order to get a number. To evaluate a sympy expression, you could use .subs() but I prefer .replace() which feels more powerful (at least for me).
You must try and make every single variable have its own name in order to avoid confusion. For example, you replace the float input t with a sympy Symbol from the very beginning, thus losing the value of t. The variables x and i are also repeated in the outer scope which is not good practice if they mean different things.
The following should avoid confusion and hopefully produce something that you were expecting:
import numpy as np
import sympy as sp
import matplotlib.pyplot as plt
from scipy.integrate import odeint
# Define model: x = [i , k]
def RLC(x, t):
# define constants first
i = x[0]
k = x[1]
L = 10e-3 # 10 mHy
C = 1.56e-6 # 1.56 uF
R1 = 1000 # 1 kOhm
R2 = 100 # 100 Ohm
# define symbols (used to find derivatives)
i_symbol = sp.Symbol('i')
t_symbol = sp.Symbol('t')
# Data (differentiate and evaluate)
E = sp.ln(t_symbol + 1)
dE_dt = E.diff(t_symbol).replace(t_symbol, t)
R = R1 * i_symbol + R2 * i_symbol ** 3
dR_di = R.diff(i_symbol).replace(i_symbol, i)
# nothing should contain symbols from here onwards
# variables can however contain sympy expressions
# Model (convert sympy expressions to floats)
di_dt = float(k)
dk_dt = float(1 / L * dE_dt - dR_di / L * k - 1 / (L * C) * i)
dx_dt = np.array([di_dt, dk_dt])
return dx_dt
# init cond:
x0 = np.array([0, 0])
# time points:
time = np.linspace(0, 30, 1000)
# solve ODE:
solution = odeint(RLC, x0, time)
result = solution[:, 0]
print(result)
Just something to note: the value i = x[0] seemed to sit very close to 0 throughout each iteration. This means dR_di stayed basically at 1000 the whole time. I'm not familiar with odeint or your specific ODE, but hopefully this phenomenon is expected and isn't a problem.

Integrate square function

I need to make the fourier series of an square function.
To get this a have a function y = square(t)
i need to make the integral of (2/p)*(square(t)*cos((2pi/p)*t)) from 0 to a variable I type for example p = 10 but i cant make this integral because every time i try to do this with scipy i get an error with the square function.
from scipy.signal import *
from scipy.integrate import quad
from numpy import *
p = 10
na = arange(0,10,1)
def integrand(t, a, b):
return square(t)*cos(b*((2*pi)/a)*t)
i,err = quad(integrand,0,p, args=(p,na))
y = i
quadpack.error: Supplied function does not return a valid float.
quad returns 2 parameters, the integral and the absolute value of the error, you just have to unpack it.
from scipy.signal import *
from scipy.integrate import quad
from numpy import *
p = 10
def integrand(t, a):
return square(t)*cos(((2*pi)/a)*t)
i, err = quad(integrand,0,p, args=(p,))
y = (2/p)*i
update:
from scipy.signal import *
from scipy.integrate import quad
from numpy import *
p = 10
na = arange(0,10,1)
y = []
def integrand(t, a, b):
return square(t)*cos(b*((2*pi)/a)*t)
for e in na:
i,err = quad(integrand,0,p, args=(p, e))
y.append(i)
print(y)

indexing in lambda function to optimize multiple variables of a sum

I want to solve an optimization problem as proposed in this thread. Now, I not only want to solve for the x[1]...x[n], but also for the variable y. It looks like something is wrong with the indexing.
from sympy import Sum, symbols, Indexed, lambdify
from scipy.optimize import minimize
import numpy as np
def _eqn(y, variables, periods, sign=-1.0):
x, i = symbols("x i")
n = periods-1
s = Sum(Indexed('x', i)/(1+0.06)**i, (i, 1, n))
f = lambdify(x, s, modules=['sympy'])
return float(sign*(y + f(variables)))
z = 3
results = minimize(lambda xy: _eqn(xy[0], xy[1:z], z),np.zeros(z))
print(results.x)
From the error message it seems there is an issue in your indexing. The summation runs from 1 to n but by default indexing of list type objects in Python goes from 0 to n-1. If I change this in your code it seems to work. Check it out.
import sympy as sp
from scipy.optimize import minimize
import numpy as np
sp.init_printing()
def _eqn(y, variables, periods, sign=-1.0):
x, i = sp.symbols("x i")
n = periods-1
s = sp.Sum(sp.Indexed('x', i)/(1+0.06)**(i+1), (i, 0, n-1)).doit()
f = sp.lambdify(x, s, modules=['sympy'])
return float(sign*(y + f(variables)))
z = 3
results = minimize(lambda xy: _eqn(xy[0], xy[1:z], z),np.zeros(z))
print(results.x)
If all you need is variable number of arguments for minimization then does the following code work for you?
from sympy import var
from scipy.optimize import minimize
import numpy as np
def _eqn(y, variables, periods, sign=-1.0):
f = 0
for i,x in enumerate(variables):
f += x/(1+0.06)**(i+1)
return float(sign*(y + f))
z = 3
results = minimize(lambda xy: _eqn(xy[0], xy[1:z], z),np.zeros(z))
print(results.x)

How to define a one variable function from another multivariable function

I am trying to define a one variable g function from a multivariable function G:
def dG(thetaf,psi,gamma) :
return 0.35*(cos(psi))**2*(2*sin(3*thetaf/2+2*gamma)+(1+4*sin(gamma)**2)*sin(thetaf/2)-sin(3*thetaf/2))+sin(psi)**2*sin(thetaf/2)
g = lambda thetaf: dG(thetaf,psi,gamma)
unfortunately this is not working and the error i receive is that :
only length-1 arrays can be converted to Python scalars
You have to define some default values. If you do this by using keyword arguments, you don't even need to define a separate function.
from numpy import sin, cos, arange
def dG(thetaf,psi=0.5,gamma=1) :
return 0.35*(cos(psi))**2*(2*sin(3*thetaf/2+2*gamma)+(1+4*sin(gamma)**2)*sin(thetaf/2)-sin(3*thetaf/2))+sin(psi)**2*sin(thetaf/2)
thetaf = arange(10)
print dG(thetaf)
>>> [ 0.4902 0.1475 0.5077 1.6392 1.757 0.4624 -0.472 -0.2416 -0.2771 -1.3398]
You actually can define a separate function, but using keyword defaults is the cleaner alternative.
g = lambda tf: dG(tf, 0.5, 1)
g(thetaf)
array([ 0.4902, 0.1475, 0.5077, 1.6392, 1.757 , 0.4624, -0.472 ,
-0.2416, -0.2771, -1.3398])
Next time, please include the script in your original question in a nice format. It makes helping go faster.
I think it is just a simple mistake. You get theta and phi out of gamma and psi respectively, but then you never use them. Did you mean to use those as your parameters in g? If so, then it should look something like this
from numpy import sin, cos, arange, linspace, pi, zeros
import scipy.optimize as opt
def dG(thetaf, psi, gamma):
return 0.35*(cos(psi))**2*(2*sin(3*thetaf/2+2*gamma)+(1+4*sin(gamma)**2)*sin(thetaf/2)-sin(3*thetaf/2))+sin(psi)**2*sin(thetaf/2)
nt = 100
np = 100
gamma = linspace(0, pi/2, nt)
psi = linspace(0, pi/2, np)
x = zeros((nt, np))
for i, theta in enumerate(gamma):
for j, phi in enumerate(psi):
print('i = %d, j = %d') %(i, j)
g = lambda thetaf: dG(thetaf,phi,theta)
x[i,j] = opt.brenth(g,-pi/2,pi/2)

Categories