Laplace transform of unknown integral (function of time) - python

I need to calculate the Laplace transform of an integral function. It seems that sympy is not yet able to understand that.
Assuming the following:
from sympy import *
s, t = symbols('s t')
I = Function('I')(t)
eq1 = integrate(I, t)
transforms.laplace_transform(eq1, t, s)
The solution should be: I(s) / s
However, sympy gives: LaplaceTransform(Integral(I(t), t), t, s)
It seems to be an open issue Issue 7219. Is there any work around?

It seems that the issue hasn't been fixed yet.
However, we can give a "crappy workaround" based on the "crappy implementation" of Eric Wieser given for derivatives. Note, however, that the original snippet doesn't seem to work for derivatives either, because the internal representation of higher order derivatives seems to have changed since the snippet was posted.
Here's my "crappy" workaround that catches only the simplest of cases (derivatives only with respect to t, indefinite integrals only with respect to t, where t is the variable on which the Laplace transform acts):
from sympy import *
def laplace(e, t, s):
"""Hacked-up Laplace transform that handles derivatives and integrals
Updated generalization of https://github.com/sympy/sympy/issues/7219#issuecomment-154768904
"""
res = laplace_transform(e, t, s, noconds=True)
wf = Wild('f')
lw = LaplaceTransform(wf, t, s)
for exp in res.find(lw):
e = exp.match(lw)[wf]
args = e.args
if isinstance(e, Derivative):
# for derivative check that there's only d/dt^n with n>0
if len(args) == 2 and args[1][0] == t:
n = args[1][1]
if n > 0:
newexp = s**n * LaplaceTransform(e.args[0], t, s)
res = res.replace(exp, newexp)
elif isinstance(e, Integral):
# for integral check that there's only n consecutive indefinite integrals w.r.t. t
if all(len(arg) == 1 and arg[0] == t for arg in args[1:]):
newexp = s**(-len(args[1:])) * LaplaceTransform(args[0], t, s)
res = res.replace(exp, newexp)
# otherwise don't do anything
return res
x = Function('x')
s,t = symbols('s t')
print(laplace(Derivative(x(t), t, 3), t, s))
print(laplace(Integral(Integral(x(t), t), t), t, s))
The above outputs
s**3*LaplaceTransform(x(t), t, s)
LaplaceTransform(x(t), t, s)/s**2
as expected. Using your specific example:
I = Function('I')(t)
eq1 = integrate(I, t)
LI = laplace(eq1, t, s)
print(LI)
we get
LaplaceTransform(I(t), t, s)/s
which is the correct representation of "I(s)/s" that you expected.
The way the above workaround works is that it matches the arguments of the LaplaceTransform and checks if there's a pure Derivative or Integral inside. For Derivative we check that there's only differentiation with respect to t; this is what Eric's original workaround did, but while his code seems to have expected args of the form Derivative(x(t), t, t, t), the current representation of derivatives is Derivative(x(t), (t,3)). This is why handling this use case had to be changed.
As for Integrals, the representation is similar to the original one: Integral(x(t), t, t) is a double integral. I still had to tweak Eric's original, because the args of this expression contain tuples for each integral rather than a scalar t, in order to accommodate definite integrals. Since we only want to handle the no-brainer case of indefinite integrals, I made sure that there's only indefinite integrals and only with respect to t.
If the argument of the LaplaceTransform is anything else, the expression is left alone.

Related

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)}

Sympy can't find inverse laplace transform of a sum of easy terms

I'm computing the inverse Laplace transform of an expression which is the sum of relatively basics terms to invert:
>>> t = symbols('t', positive=True)
>>> s = symbols('s')
>>> inverse_laplace_transform(1 - 9/(s + 2) + 5/(s+1) - 1/(s+1)**2, s, t)
It seems to be able to compute each individual fine:
>>> inverse_laplace_transform(-1/(s+1)**2, s, t)
-t*exp(-t)
>>> inverse_laplace_transform(5/(s-1), s, t)
5*exp(t)
>>> inverse_laplace_transform(9/(s+2), s, t)
9*exp(-2*t)
>>> inverse_laplace_transform(1, s, t)
InverseLaplaceTransform(1, s, t, _None)
Yet when I take the sum of these, I get a long error saying it can't find the polynomial
inverse_laplace_transform(1 - 9/(s + 2) + 5/(s+1) - 1/(s+1)**2, s, t)
sympy.polys.polyerrors.PolynomialDivisionFailed: couldn't reduce degree in a polynomial division algorithm when dividing [EX(-1728*3**(1/3)(9 + sqrt(93))*(1/3)*(27 + 3*sqrt(93))**(2/3) + 1728*3**(5/6)I(9 + sqrt(93))**(1/3)*(27 + 3*sqrt(93))**(2/3))] by [EX(1)].
This can happen when it's not possible to detect zero in the coefficient domain. The domain of computation is EX.
You may want to use a different simplification algorithm.
Note that in general it's not possible to guarantee to detect zero in this domain.
Does anyone know a reason why sympy shouldn't be able to do this on the sum of the parts when it can do it on each part separately?
Probably a bug in newer version. SymPy 1.0 used to work just fine:
>>> inverse_laplace_transform(1 - 9/(s + 2) + 5/(s+1) - 1/(s+1)**2, s, t)
-t*exp(-t) + InverseLaplaceTransform(1, s, t, _None) + 5*exp(-t) - 9*exp(-2*t)

Why doesn't sympy simplify the Fourier Transform of a derivative?

We know that the Fourier Transform of a derivative is
where k is the fourier variable. Explanation here
My question is, why doesn't sympy use this knowledge? For example:
from sympy import Function, symbols, fourier_transform, Derivative
f = Function('f')
x, k= symbols('x, k')
G = fourier_transform(Derivative(f(x), x, x) + f(x), x, k)
print(G)
This prints
FourierTransform(f(x), x, k) + FourierTransform(Derivative(f(x), x, x), x, k)
But I expected it to print (up to some factors of 2 pi i)
FourierTransform(f(x), x, k) + k**2 FourierTransform(f(x), x, k)
Is there a way to tell sympy it's save to make this simplification because I expect f(x) -> 0 as x goes to infinity?
If not, what would be the cleanest way to make the substitution?
The simple reason Sympy doesn't do this is that it's not implemented yet. As a workaround for now, you can manually replace the FourierTransform of the derivative with a multiplication:
from sympy import Wild, FourierTransform, Derivative
a, b, c = symbols('a b c', cls=Wild)
G.replace(
FourierTransform(Derivative(a, b, b), b, c),
c**2 * FourierTransform(a, b, c)
)
As far as I know, Sympy doesn't offer a pattern that matches an arbitrary number of arguments, so you can't have a single pattern that matches Derivative(f(x), x), Derivative(f(x), x, x), Derivative(f(x), x, x, x), and so on. You could get around that by using the function-function form of replace(), but if you know what order of derivative you're dealing with, it's probably simpler to just put in that many bs explicitly, as I did in the example.

Solving a system of odes (with changing constant!) using scipy.integrate.odeint?

I currently have a system of odes with a time-dependent constant. E.g.
def fun(u, t, a, b, c):
x = u[0]
y = u[1]
z = u[2]
dx_dt = a * x + y * z
dy_dt = b * (y-z)
dz_dt = -x*y+c*y-z
return [dx_dt, dy_dt, dz_dt]
The constants are "a", "b" and "c". I currently have a list of "a"s for every time-step which I would like to insert at every time-step, when using the scipy ode solver...is this possible?
Thanks!
Yes, this is possible. In the case where a is constant, I guess you called scipy.integrate.odeint(fun, u0, t, args) where fun is defined as in your question, u0 = [x0, y0, z0] is the initial condition, t is a sequence of time points for which to solve for the ODE and args = (a, b, c) are the extra arguments to pass to fun.
In the case where a depends on time, you simply have to reconsider a as a function, for example (given a constant a0):
def a(t):
return a0 * t
Then you will have to modify fun which computes the derivative at each time step to take the previous change into account:
def fun(u, t, a, b, c):
x = u[0]
y = u[1]
z = u[2]
dx_dt = a(t) * x + y * z # A change on this line: a -> a(t)
dy_dt = b * (y - z)
dz_dt = - x * y + c * y - z
return [dx_dt, dy_dt, dz_dt]
Eventually, note that u0, t and args remain unchanged and you can again call scipy.integrate.odeint(fun, u0, t, args).
A word about the correctness of this approach. The performance of the approximation of the numerical integration is affected, I don't know precisely how (no theoretical guarantees) but here is a simple example which works:
import matplotlib.pyplot as plt
import numpy as np
import scipy as sp
import scipy.integrate
tmax = 10.0
def a(t):
if t < tmax / 2.0:
return ((tmax / 2.0) - t) / (tmax / 2.0)
else:
return 1.0
def func(x, t, a):
return - (x - a(t))
x0 = 0.8
t = np.linspace(0.0, tmax, 1000)
args = (a,)
y = sp.integrate.odeint(func, x0, t, args)
fig = plt.figure()
ax = fig.add_subplot(111)
h1, = ax.plot(t, y)
h2, = ax.plot(t, [a(s) for s in t])
ax.legend([h1, h2], ["y", "a"])
ax.set_xlabel("t")
ax.grid()
plt.show()
I Hope this will help you.
No, that is not possible in the literal sense of
"I currently have a list of "a"s for every time-step which I would like to insert at every time-step"
as the solver has adaptive step size control, that is, it will use internal time steps that you have no control over, and each time step uses several evaluations of the function. Thus there is no connection between the solver time steps and the data time steps.
In the extended sense that the given data defines a piecewise constant step function however, there are several approaches to get to a solution.
You can integrate from jump point to jump point, using the ODE function with the constant parameter for this time segment. After that use numpy array operations like concatenate to assemble the full solution.
You can use interpolation functions like numpy.interp or scipy.interpolate.interp1d. The first gives a piecewise linear interpolation, which may not be desired here. The second returns a function object that can be configured to be a "zero-order hold", which is a piecewise constant step function.
You could implement your own logic to go from the time t to the correct values of those parameters. This mostly applies if there is some structure to the data, for instance, if they have the form f(int(t/h)).
Note that the approximation order of the numerical integration is not only bounded by the order of the RK (solve_ivp) or multi-step (odeint) method, but also by the differentiability order of the (parts of) the differential equation. If the ODE is much less smooth than the order of the method, the implicit assumptions for the step size control mechanism are violated, which may result in a very small step size requiring a huge number of integration steps.
I also encountered similar problem. In my case, parameters a, b, and c are not in direct function with time, but determined by x, y, and z at that time. So I have to get x, y, z at time t, and calculate a, b, c for the integration calculation for x, y, z at t+dt. It turns out that if I change dt value, the whole integration result will change dramatically, even to something unreasonable.

calculate an integral in python

I need to calculate an integral in python.
I have imported sympy.
g(a,z) = integral_from_z_to_inf of ( t^(a-1) * e^(-1))
in python:
x,a,z = symbols('x a z')
g = integrate(x**(a-1) * exp(-x), z, oo)
I got error:
ValueError: Invalid limits given: (z, oo)
I called:
b,c,mean,variance = S('b c mean variance'.split())
ff1 = b*g((1+c), lb / b) // lb is a constant, b and c are unknown var that I need to solve. and mean and variance are constants.
I got error:
TypeError: 'Add' object is not callable
I am on Python 2.7, but your problems seems to be not reading the documentation closely enough. The docs say:
var can be:
a symbol – indefinite integration
a tuple (symbol, a) – indefinite integration with result
given with a replacing symbol
a tuple (symbol, a, b) – definite integration
You want to perform the last one, so you need to use a tuple.
The command you are looking for is:
import sympy as sym
x, a, z = sym.symbols('x a z')
g = sym.integrate(x**(a-1) * sym.exp(-x), (x, z, sym.oo))

Categories