Ultimately, my goal is to numerically differentiate the expression \'u\' (see code) with respect to t, with respect to X and three times with respect to X.
First ide
SymPy and NumPy are completely separate libraries. SymPy flourishes in the world of symbolic math and works with its own symbols for every part of mathematical expressions.
The only place where SymPy and NumPy touch, is lambdify
where everything is converted to NumPy symbols, ready to go number crunching.
The function u
doesn't need a symbol: it gets its SymPy representation via its definition based on t
and X
.
The differentiation happens completely inside SymPy, e.g. diff(u, X, 3)
calculates the third derivative of u
with respect to X
. simplify
helps to reduce the size of the expression. However, the expression for du_ffffdX
seems so long that simplification takes a huge amount of time. If you don't need to call the function millions of times, you can leave it without simplification.
import numpy as np
import sympy as sp
c_1 = 1.35
c_2 = 0.7
X = sp.Symbol('X', real=True)
t = sp.Symbol('t', real=True)
u = 2*(c_1-c_2)*(c_1*(sp.cosh(sp.sqrt(c_2)*(X-c_2*t)/2))**2 + c_2*(sp.sinh(sp.sqrt(c_1)*(-X-c_1*t)/2))**2)/((sp.sqrt(c_1)-sp.sqrt(c_2))*sp.cosh((sp.sqrt(c_1)*(-X-c_1*t) + sp.sqrt(c_2)*(X-c_2*t))/2)+ (sp.sqrt(c_1)+sp.sqrt(c_2))*sp.cosh((sp.sqrt(c_1)*(-X-c_1*t)-sp.sqrt(c_2)*(X-c_2*t))/2))**2
du_dt = sp.simplify(sp.diff(u, t))
du_dX = sp.simplify(sp.diff(u, X))
du_ffffdX = sp.diff(u, X, 3)
#du_ffffdX = sp.simplify(du_ffffdX)
U = sp.lambdify((X,t), u, "numpy")
U1 = sp.lambdify((X,t), du_dt, "numpy")
U2 = sp.lambdify((X,t), du_dX, "numpy")
U3 = sp.lambdify((X,t), du_ffffdX, "numpy")
# before this line, everything happened in SymPy
# now the NumPy part starts
Y = np.linspace(-20, 20, 20)
T = np.linspace(-35, 35, 20)
print(U(Y, T))
print(U1(Y, T))
print(U2(Y, T))
print(U3(Y, T))
Note that the linspace for Y and for T need to have the same size if you want to call the lambdified functions directly on them. You probably want to extend the 1D linspaces to a 2D mesh using np.meshgrid(). The mesh can have a different number of divisions in the two directions. An example with your function:
import matplotlib.pyplot as plt
Y = np.linspace(-20, 20, 100)
T = np.linspace(-35, 35, 300)
YY, TT = np.meshgrid(Y, T)
z = U1(YY, TT)
h = plt.contourf(Y,T,z)
plt.show()
PS: To convert the expressions to LaTeX, although the are quite long:
print(sp.latex(du_dt))
print(sp.latex(du_dX))
u
is sympy
expression. U
in python/numpy. The sp.sinh
etc are translated to np.sinh
etc.
U(Y)
evaluates this with the numpy
array, but t
is still a symbol. That produces a numpy
object dtype array, with some sort of mix of numbers and symbols. np.sinh(x)
is evaluated as [z.sinh() for z in x]
. Since most objects, including symbols don't have a sinh
method, this raises your error.
I'm not sure about this, but I suspect you need to lambdify
both X
and t
at once, and evaluate with (Y,T)
together, rather than in two steps.
(Later I may try to demonstrate this with a isympy
session.)