I am doing some symbolic vector calculations using sympy, but I can't simplify the arguments of the vector class in a proper way. Consider this code:
from sympy.physics.mechanics import ReferenceFrame, dot, cross
from sympy import symbols, sin, cos, simplify
alpha, theta, l = symbols('alpha theta l')
def Rodrigues(v, k, angle):
return cos(angle) * v + cross(k, v) * sin(angle) + k * dot(k, v) * (1- cos(angle))
N = ReferenceFrame('N')
P0 = -l * N.y
P2 = Rodrigues(
Rodrigues(P0, -N.z, alpha),
Rodrigues(N.x, -N.z, alpha),
theta)
which returns:
trying the simplify(P2) I get the error:
AttributeError: 'function' object has no attribute 'x'
which I think is because the simplify requires a sympy expression object. trying the dir(P2) there is a simplify method which returns:
<bound method Vector.simplify of - l*sin(alpha)*cos(theta)*N.x - l*cos(alpha)*cos(theta)*N.y + (-l*sin(alpha)**2 - l*cos(alpha)**2)*sin(theta)*N.z>
which I have no idea what it is! trying the P2.args I get:
[(Matrix([
[ -l*sin(alpha)*cos(theta)],
[ -l*cos(alpha)*cos(theta)],
[(-l*sin(alpha)**2 - l*cos(alpha)**2)*sin(theta)]]), N)]
which is a 1D List of a 2D tuple with a nested 3x1 sympy Matrix! I don't know whos choice was to make the vector class so obscure, but now I can simplify the last element with simplify(P2.args[0][0][2]) and change the function to:
def Rodrigues(v, k, angle):
tmpVec = cos(angle) * v + cross(k, v) * sin(angle) + k * dot(k, v) * (1- cos(angle))
tmpFrame = tmpVec.args[0][1]
return simplify(tmpVec.args[0][0][0]) * tmpFrame.x + simplify(tmpVec.args[0][0][1]) * tmpFrame.y + simplify(tmpVec.args[0][0][2]) * tmpFrame.z
which to me seems like a very bad solution.
I was wondering if you could help me know if there is a more Pythonic way to do this. For example, force sympy to simplify all expressions by default. Or maybe I'm using the vector.simplify method in a wrong way? Thanks for your support in advance.
P.S. Rodrigues rotation formula
Instead of print(P2.simplify) you need to call this method like print(P2.simplify()). After that you will get - l*sin(alpha)*cos(theta)*N.x - l*cos(alpha)*cos(theta)*N.y - l*sin(theta)*N.z as output which is the same to last version of your def Rodrigues.
An alternative solution is to force sympy to simplify all vectors by default:
from sympy.physics.vector import Vector
Vector.simp = True
more info here
Related
I have the following expression:
T = 0.5*m*(r(t)**2*Derivative(theta(t), t)**2 + Derivative(r(t), t)**2)
I would like to extract the coefficients of Derivative(theta(t), t) and Derivative(r(t), t) to get:
0.5*m*r(t)**2 and 0.5*m, respectively.
I tried:
cr = T.coeff(Derivative(r(t), t),2)
ctheta = T.coeff(Derivative(theta(t), t),2)
but I get the following error:
'r' object is not callable
Is there a way to accomplish this?
Thanks!
Check which version of sympy you are using with import sympy;print(sympy.__version__). You might want to update to version 1.11.1.
from sympy import *
t, m = symbols("t, m")
r, theta = [Function(e) for e in ["r", "theta"]]
T = m / 2 * (r(t)**2 * Derivative(theta(t), t)**2 + Derivative(r(t), t)**2)
Let's expand the expression and then call the coeff method:
T.expand().coeff(Derivative(r(t), t), 2)
# out: m/2
T.expand().coeff(Derivative(theta(t), t), 2)
# out: m*r(t)**2/2
I would like to solve the (x+1)e^x=c equation in Python.
The equation has been successfully solved by hand using lambert w functions as depicted in the figure below:
Using same steps, I would like to solve (x+1)e^x programmatically. I have coded it using the module SymPy as per the step shown in the figure above , but without success.
Is there any to solve these kinds of equations in Python?
import numpy as np
from sympy import *
n = symbols('n')
sigmao=0.06866
sigmas=0.142038295
theta=38.9
rad=(np.pi/180)*38.9076
cos=np.cos(rad)
sec=1/np.cos(rad)
out = (0.06*0.7781598455*n*(1-exp(-2*0.42*sec*n))+exp(-2*0.42*n*sec)*sigmas)/sigmao
#Apply diff for the above expression.
fin=diff(out, n)
print(solve(fin,n))
from scipy.optimize import fsolve
import numpy as np
const = 20
def func(x):
return [(x[0]+1) * np.exp(x[0]) - const]
result = fsolve(func, [1])[0]
print('constant: ', const, ', solution: ', result)
#check
print('check: ', (result+1) * np.exp(result))
#Output[]:
constant: 20.0 , solution: 1.9230907433218063
check: 20.0
Preview : https://onlinegdb.com/By8Z2Jwgw
Your expression is very numeric. As sympy's solve tries to find a perfect symbolic solution, sympy gets into troubles.
To find numeric solutions, sympy has nsolve (which allows sympy's expressions but behind the scenes calls mpmath's numeric solver). Unlike solve, here an initial guess is needed:
from sympy import symbols, exp, diff, nsolve, pi, cos
n = symbols('n')
sigmao = 0.06866
sigmas = 0.142038295
theta = 38.9076
rad = (pi / 180) * theta
sec = 1 / cos(rad)
out = (0.06 * 0.7781598455 * n * (1 - exp(-2 * 0.42 * sec * n)) + exp(-2 * 0.42 * n * sec) * sigmas) / sigmao
# Apply diff for the above expression.
fin = diff(out, n)
result = nsolve(fin, n, 1)
print(result, fin.subs(n, result).evalf())
Result: 1.05992379637846 -7.28565300819065e-17
Note that when working with numeric values, you should be very careful to use as many digits as possible to avoid accumulation of errors. Whenever you have an exact expression, it is recommended to leave that expression into the code, instead of replacing it with digits. (Usually, 64 bits or about 16 digits are used in calculations, but for intermediate calculations 80 bits can be taken into account).
To solve the original question with sympy:
from sympy import symbols, Eq, exp, solve
x = symbols('x')
solutions = solve(Eq((x + 1) * exp(x), 20))
for s in solutions:
print(s.evalf())
Result: 1.92309074332181
I'm trying to solve an integral with sympy. But it gives me a wrong solution. Why?
import sympy
from sympy import Integral, exp, oo
x, y = sympy.symbols("x y", real=True)
b, u, l, t = sympy.symbols("b u l t ", real=True, positive=True)
Fortet = Integral(exp(-l * t) * (sympy.sqrt(2 * sympy.pi * t)) ** (-1) * exp(-((b - u * t - y) ** 2) / (2 * t)),
(t, 0, oo))
Fortet.doit()
Result (wrong):
Piecewise((-(-b/2 + y)*sqrt(2*l +
u**2)*(-sqrt(pi)*sinh(sqrt(2)*sqrt(b)*sqrt(l +
u**2/2)*sqrt(polar_lift(1 + y**2/(b*polar_lift(b -
2*y))))*sqrt(polar_lift(b - 2*y))) +
sqrt(pi)*cosh(sqrt(2)*sqrt(b)*sqrt(l + u**2/2)*sqrt(polar_lift(1 +
y**2/(b*polar_lift(b - 2*y))))*sqrt(polar_lift(b - 2*y))))*exp(b*u -
u*y)/(sqrt(pi)*(b - 2*y)*(l + u**2/2)), Abs(arg(1 +
y**2/(b*polar_lift(b - 2*y))) + arg(b - 2*y)) <= pi/2),
(Integral(sqrt(2)*exp(-l*t)*exp(-(b - t*u -
y)**2/(2*t))/(2*sqrt(pi)*sqrt(t)), (t, 0, oo)), True))
Expected (correct) solution:
Solution = (exp((-u)*(b - y)) * exp(sympy.sqrt(u**2 + 2*l)*(b-y)))/(sympy.sqrt(2*l + u**2)) #RIGHT solution
Both results are in fact the same. The first one is probably slightly more correct. You tend see these polar_lift functions whenever SymPy tries to do something like square rooting something when it does not know the signs of the things inside (after integrating)
A polar_lift does not appear below, but this basic Gaussian example shows that SymPy tries to be as general as possible:
from sympy import *
x = Symbol("x", real=True)
y = Symbol("y", real=True)
s = Symbol("s", real=True) # , positive=True
gaussian = exp(-((x-y)**2)/(2*(s**2)))
nfactor = simplify(integrate(gaussian, (x,-oo,oo)))
print(nfactor)
You need s to be declared as positive: s = Symbol("s", real=True, positive=True). A similar thing happens with these kinds of polar_lift(b - 2*y) functions in your example. It also happens with the question I reference below.
I have no idea why, but N(polar_lift(x)) for any float or int x gives x again; yet, SymPy does not simplify nicely with symbolic x. Turns out if you keep on simplifying, you get nicer and nicer looking answers. I couldn't find anything about polar_lift related to pure math so I don't know what it actually does.
Remember for the simple example above how it gave a piece-wise? Same thing here. So we just take the first piece since the second piece is an un-evaluated integral.
In the code below, I use this question to remove the piece-wise function and then I simplify twice. And finally, I manually remove the polar_lift.
import sympy as sp
x, y = sp.symbols("x y", real=True)
b, u, l, t = sp.symbols("b u l t ", real=True, positive=True)
Fortet = sp.integrate(sp.exp(-l * t) * (sp.sqrt(2 * sp.pi * t)) ** (-1) *
sp.exp(-((b - u * t - y) ** 2) / (2 * t)),
(t, 0, sp.oo), conds='none')
incorrect = Fortet.simplify().simplify()
correct = eval(str(incorrect).replace("polar_lift", ""))
correct = correct.factor()
print(correct)
The result is:
exp(b*u)*exp(-u*y)*exp(-sqrt(2*l + u**2)*sqrt(b**2 - 2*b*y + y**2))/sqrt(2*l + u**2)
That is close enough to your expression. I couldn't make SymPy simplify the sqrt(b**2 - 2*b*y + y**2) to Abs(b-y) no matter how hard I tried.
Note that either SymPy is still wrong or you are wrong since the powers in the numerator are opposite. So I checked on the Desmos for a numeric answer (top one is yours):
The idea is to compute the line integral of the following vector field and curve:
This is the code I have tried:
import numpy as np
from sympy import *
from sympy import Curve, line_integrate
from sympy.abc import x, y, t
C = Curve([cos(t) + 1, sin(t) + 1, 1 - cos(t) - sin(t)], (t, 0, 2*np.pi))
line_integrate(y * exp(x) + x**2 + exp(x) + z**2 * exp(z), C, [x, y, z])
But the ValueError: Function argument should be (x(t), y(t)) but got [cos(t) + 1, sin(t) + 1, -sin(t) - cos(t) + 1] comes up.
How can I compute this line integral then?
I think that maybe this line integral contains integrals that don't have exact solution. It is also fine if you provide a numerical approximation method.
Thanks
In this case you can compute the integral using line_integrate because we can reduce the 3d integral to a 2d one. I'm sorry to say I don't know python well enough to write the code, but here's the drill:
If we write
C(t) = x(t),y(t),z(t)
then the thing to notice is that
z(t) = 3 - x(t) - y(t)
and so
dz = -dx - dy
So, we can write
F.dr = Fx*dx + Fy*dy + Fz*dz
= (Fx-Fz)*dx + (Fy-Fz)*dy
So we have reduced the problem to a 2d problem: we integrate
G = (Fx-Fz)*i + (Fx-Fz)*j
round
t -> x(t), y(t)
Note that in G we need to get rid of z by substituting
z = 3 - x - y
The value error you receive does not come from your call to the line_integrate function; it comes because according to the source code for the Curve class, only functions in 2D Euclidean space are supported. This integral can still be computed without using sympy according to this research blog that I found by simply searching for a workable method on Google.
The code you need looks like this:
import autograd.numpy as np
from autograd import jacobian
from scipy.integrate import quad
def F(X):
x, y, z = X
return [y * np.exp(x), x**2 + np.exp(x), z**2 * np.exp(z)]
def C(t):
return np.array([np.cos(t) + 1, np.sin(t) + 1, 1 - np.cos(t) - np.sin(t)])
dCdt = jacobian(C, 0)
def integrand(t):
return F(C(t)) # dCdt(t)
I, e = quad(integrand, 0, 2 * np.pi)
The variable I then stores the numerical solution to your question.
You can define a function:
import sympy as sp
from sympy import *
def linea3(f,C):
P = f[0].subs([(x,C[0]),(y,C[1]),(z,C[2])])
Q = f[1].subs([(x,C[0]),(y,C[1]),(z,C[2])])
R = f[2].subs([(x,C[0]),(y,C[1]),(z,C[2])])
dx = diff(C[0],t)
dy = diff(C[1],t)
dz = diff(C[2],t)
m = integrate(P*dx+Q*dy+R*dz,(t,C[3],C[4]))
return m
Then use the example:
f = [x**2*z**2,y**2*z**2,x*y*z]
C = [2*cos(t),2*sin(t),4,0,2*sp.pi]
How do I simplify a*sin(wt) + b*cos(wt) into c*sin(wt+theta) using SymPy? For example:
f = sin(t) + 2*cos(t) = 2.236*sin(t + 1.107)
I tried the following:
from sympy import *
t = symbols('t')
f=sin(t)+2*cos(t)
trigsimp(f) #Returns sin(t)+2*cos(t)
simplify(f) #Returns sin(t)+2*cos(t)
f.rewrite(sin) #Returns sin(t)+2*sin(t+Pi/2)
PS.: I dont have direct access to a,b and w. Only to f
Any suggestion?
The general answer can be achieved by noting that you want to have
a * sin(t) + b * cos(t) = A * (cos(c)*sin(t) + sin(c)*cos(t))
This leads to a simultaneous equation a = A * cos(c) and b = A * sin(c).
Dividing the second equation by the second, we can solve for c. Substituting its solution into the first equation, you can solve for A.
I followed the same pattern but just to get it in terms of cos. If you want to get it in terms of sin, you can use Rodrigo's formula.
The following code should be able to take any linear combination of the form x * sin(t - w) or y * cos(t - z). There can be multiple sins and cos'.
from sympy import *
t = symbols('t', real=True)
expr = sin(t)+2*cos(t) # unknown
d = collect(expr.expand(trig=True), [sin(t), cos(t)], evaluate=False)
a = d[sin(t)]
b = d[cos(t)]
cos_phase = atan(a/b)
amplitude = a / sin(cos_phase)
print(amplitude.evalf() * cos(t - cos_phase.evalf()))
Which gives
2.23606797749979*cos(t - 0.463647609000806)
This seems to be a satisfactory match after plotting both graphs.
You could even have something like
expr = 2*sin(t - 3) + cos(t) - 3*cos(t - 2)
and it should work fine.
a * sin(wt) + b * cos(wt) = sqrt(a**2 + b**2) * sin(wt + acos(a / sqrt(a**2 + b**2)))
While the amplitude is the radical sqrt(a**2 + b**2), the phase is given by the arccosine of the ratio a / sqrt(a**2 + b**2), which may not be expressible in terms of arithmetic operations and radicals. Hence, you may be asking SymPy to do the impossible. Better use floating-point values, but you do not need SymPy for that.