Linde
Linde

Reputation: 45

"Add" object has no attribute "sinh" error in numerical and symbolic expression?

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 idea was to just write the expression down numerically, providing arrays (linspaces) for X and t. This resulted in the error "'Add' object has no attribute 'cosh'". The only thing I know about this error is that it indicates I should use sympy-functions instead of numpy-functions or the other way around. But, using a symbpolic expression (sympy-functions) and then trying to lambdify gave the same error, this time with no attribute 'sinh'.

I don't know where I'm going wrong with this. The symbolic expression is defined just fine, the error only occurs when I add the first lambdify into the code.

import numpy as np
import sympy as sp
c_1=1.35
c_2=0.7
X = sp.Symbol('X')
t = sp.Symbol('t')
u = sp.Function('u')(X,t)
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
Y= np.linspace(-20,20,100)
T = np.linspace(-35,35,300)
U = sp.lambdify(X,u,"numpy")
U2 = sp.lambdify(t,U(Y),"numpy")(T)

Does anybody know how to fix my code to prevent this error, or know another method to numerically differentiate u as I described above?

Upvotes: 0

Views: 2080

Answers (2)

JohanC
JohanC

Reputation: 80449

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_dddX 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_dddX = sp.diff(u, X, 3)
#du_dddX = sp.simplify(du_dddX)

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_dddX, "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))

Upvotes: 3

hpaulj
hpaulj

Reputation: 231615

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

Upvotes: 0

Related Questions