Sympy not solving a ODE correctly? - python

I want to solve the ode f´´(x) + k*f(x) = 0.
which is a trivial ODE to solve (https://www.wolframalpha.com/input?i=f%60%60%28x%29+%2B+kf%28x%29%3D0)
my code is
from sympy import *
x,t,k,L,C1,C2 = symbols("x,t,k,L,C1,C2")
f=symbols('f', cls=Function)
g=symbols('g', cls=Function)
Fx = f(x).diff(x)
Fxx = f(x).diff(x,x)
Gtt = g(t).diff(t,t)
Gt = g(t).diff(t)
BC1 = 0
BC2 = L
Eq1_k_positive = dsolve(Eq1.subs(k,-k))
display(Eq1_k_positive)
Not really sure why I don't get the solution that I should get. and no its not the same when I use BCs that would get me a result I get 0 since I don't get the sin cos equation. any tips on what's not correct?

This is your differential equation:
In [18]: k, x = symbols('k, x')
In [19]: f = Function('f')
In [20]: eq = Eq(f(x).diff(x, 2) + k*f(x), 0)
In [21]: eq
Out[21]:
2
d
k⋅f(x) + ───(f(x)) = 0
2
dx
This is the solution returned by SymPy:
In [22]: dsolve(eq)
Out[22]:
____ ____
-x⋅╲╱ -k x⋅╲╱ -k
f(x) = C₁⋅ℯ + C₂⋅ℯ
That solution is correct for any nonzero complex number k.
There can be many equivalent forms to represent the general solution of an ODE. SymPy will choose a different form here if you specify something about the symbol k such as that it is positive:
In [24]: k = symbols('k', positive=True)
In [25]: eq = Eq(f(x).diff(x, 2) + k*f(x), 0)
In [26]: eq
Out[26]:
2
d
k⋅f(x) + ───(f(x)) = 0
2
dx
In [27]: dsolve(eq)
Out[27]: f(x) = C₁⋅sin(√k⋅x) + C₂⋅cos(√k⋅x)
This solution is also correct for any nonzero complex number k but will only be returned if k is declared positive because it is only for positive k that there is any reason to prefer the sin/cos form to the exp form.

Related

how to solve a system of differential ecuations and a linear equation in Python

I tried to solve this equation system that are two ordinary differential equations and a linear equation
dx(t)/dt = (-x(t) + u(t)) / 2.0,
dy(t)/dt = (-y(t) + x(t)) / 5.0
u(t) = x(t) + 3*y(t)
With this initial conditions x(0) = 0, y(0) = 0 and u(0)=1. I have been trying to solve the system using odeint but I don't know hoy to define the linear function, this is, the function u. I don't know if it is another way to solve this system.
import numpy as np
from scipy.integrate import odeint
import matplotlib.pyplot as plt
def f(z,t,u):
dzdt = np.zeros(2,)
x = z[0]
y = z[1]
dzdt[0] = (-x + u) / 2.0
dzdt[1] = (-y + x) / 5.0
return dzdt
z0 = [0,0]
n = 150
t = np.linspace(0,15, n)
u = np.zeros(n)
u[0] = 1
x = np.zeros(n)
y = np.zeros(n)
for i in range (1,n):
tspan = [t[i-1],t[i]]
z = odeint(f,z0,t,args=(u[i],))
u[i] = x[i] + 3*y[i]
z0 = z[1]
x[i] = z0[0]
y[i] = z0[1]
plt.plot(t,u)
plt.plot(t,x)
plt.plot(t,y)
plt.show()
You can just substitute u into the other equations. Also these particular ODEs can be solved symbolically using SymPy:
In [34]: x, y, u = symbols('x, y, u', cls=Function)
In [35]: t = symbols('t')
In [36]: eqs = [Eq(x(t).diff(t), (-x(t) + u(t)) / 2.0),
...: Eq(y(t).diff(t), (-y(t) + x(t)) / 5.0),
...: Eq(u(t), x(t) + 3*y(t))]
In [37]: eqs[2]
Out[37]: u(t) = x(t) + 3⋅y(t)
In [38]: eqs_new = [eq.subs(eqs[2].lhs, eqs[2].rhs) for eq in eqs[:2]]
In [39]: eqs_new
Out[39]:
⎡d d ⎤
⎢──(x(t)) = 1.5⋅y(t), ──(y(t)) = 0.2⋅x(t) - 0.2⋅y(t)⎥
⎣dt dt ⎦
In [41]: dsolve(eqs_new)[0]
Out[41]:
-0.656776436283002⋅t 0.456776436283002⋅t
x(t) = - 2.28388218141501⋅C₁⋅ℯ + 3.28388218141501⋅C₂⋅ℯ
In [42]: dsolve(eqs_new)[1]
Out[42]:
-0.656776436283002⋅t 0.456776436283002⋅t
y(t) = 1.0⋅C₁⋅ℯ + 1.0⋅C₂⋅ℯ

How to differentiate a expression with respect to a symbol that is not a free symbol of the expression in SymPy?

I have the following equation, like this:
y = 3x2 + x
Then, I want to differentiate the both side w.r.t the variable t with sympy. I try to implement it in the following code in JupyterNotebook:
>>> import sympy as sp
>>> x, y, t = sp.symbols('x y t', real=True)
>>> eq = sp.Eq(y, 3 * x **2 + x)
>>>
>>> expr1 = eq.lhs
>>> expr1
𝑦
>>> expr1.diff(t)
0
>>>
>>> expr2 = eq.rhs
>>> expr2
3𝑥^2+𝑥
>>> expr2.diff(t)
0
As the result, sympy will treat the symbol x and y as a constant. However, the ideal result I want should be the same as the result derived manually like this:
y = 3x2 + x
d/dt (y) = d/dt (3x2 + x)
dy/dt = 6 • x • dx/dt + 1 • dx/dt
dy/dt = (6x + 1) • dx/dt
How can I do the derivative operation on a expression with a specific symbol which is not a free symbol in the expression?
You should declare x and y as functions rather than symbols e.g.:
In [8]: x, y = symbols('x, y', cls=Function)
In [9]: t = symbols('t')
In [10]: eq = Eq(y(t), 3*x(t)**2 + x(t))
In [11]: eq
Out[11]:
2
y(t) = 3⋅x (t) + x(t)
In [12]: Eq(eq.lhs.diff(t), eq.rhs.diff(t))
Out[12]:
d d d
──(y(t)) = 6⋅x(t)⋅──(x(t)) + ──(x(t))
dt dt dt
https://docs.sympy.org/latest/modules/core.html#sympy.core.function.Function
Alternatively, the idiff function was made for this purpose but it works with expressions like f(x, y) and can return the value of dy/dx. So first make your Eq and expression and then calculate the desired derivative:
>>> from sympy import idiff
>>> e = eq.rewrite(Add)
>>> dydx = idiff(e, y, x); dydx
6*x + 1
Note, too, that even in your equation (if you write it explicitly in terms of functions of t) you do not need to isolate y(t) -- you can differentiate and solve for it:
>>> from sympy.abc import t
>>> x,y=map(Function,'xy')
>>> eq = x(t)*(y(t)**2 - y(t) + 1)
>>> yp=y(t).diff(t); Eq(yp, solve(eq.diff(t),yp)[0])
Eq(Derivative(y(t), t), (-y(t)**2 + y(t) - 1)*Derivative(x(t), t)/((2*y(t) - 1)*x(t)))

vectorized quadratic formula, why is the runtime warning invalid value in numpy.sqrt() still raised?

numpy version 1.20.1
Bob lawblaw's Law Blog, I need more details, this post has tooooo much code.
def quadratic_formula(a, b, c):
"""just like the song
Args:
a (numpy.ndarray): shape(N,1)
b (numpy.ndarray): shape(N,1)
c (numpy.ndarray): shape(N,1)
Returns:
numpy.ndarray: shape(N,2)
* [soln_a, soln_b]
* [np.NaN, np.NaN] when discriminant is negative
* [soln_a, soln_a] single soln when discriminate == 0]
Notes:
.. math::
ax^2 + bx + c = 0
solns = \\frac{-b \\pm \\sqrt{b^2 -4ac}}{2a}
"""
# TODO: raise value error if any a == 0
a = np.array(a)
b = np.array(b)
c = np.array(c)
det = b ** 2 - 4 * a * c
res_a = np.where(
det >= 0,
(-b + np.sqrt(det)) / (2 * a),
np.NaN,
)
res_b = np.where(
det >= 0,
(-b - np.sqrt(det)) / (2 * a),
np.NaN,
)
res = np.array([res_a, res_b]).T
return res
a = [1,2]
b = [1,0]
c = [0,1]
res = quadratic_formula(a,b,c)
print(res)
>>> [[0, -1],
[NaN, NaN]]
works, but raise RuntimeWarning: invalid value encountered in sqrt.
Why is the square root even evaluated for a negative discriminant?
Any suggestions for implementation?
Note that you are still computing np.sqrt(det) for all values of det hence the warning. The where filters the x and y arrays after they have been computed.
The implementation can be fixed by simply casting the a,b and c arrays to complex.
a = np.array(a).astype(complex)
b = np.array(b).astype(complex)
c = np.array(c).astype(complex)
That way numpy knows to use the complex version of sqrt. Once you are there you can completely omit the np.where and check after the fact if your solutions are real, if that is what you are interested on only.

Sympy differentiation, equation is in numpy array?

I have a function that is updated -- I'm optimizing using the method of newton and steepest.
I'm new to sympy, and hoping to get some quick clarification.
x1,x2 = sym.symbols('x1 x2')
y = 2*x1 + 3*x2**2 + sym.exp(2*x1**2 + x2**2)
gy1 = sym.diff(y,x1)
gy2 = sym.diff(y,x2)
grad1 = sym.lambdify([x1,x2],gy1)(x[0],x[1])
grad2 = sym.lambdify([x1,x2],gy2)(x[0],x[1])
d = np.array([-1*grad1,-1*grad2])
l = sym.symbols('l')
theta = 2*(x[0]+l*d[0]) + 3*(x[1]+l*d[1])**2 + sym.exp(2*(x[0]+l*d[0])**2 + (x[1]+l*d[1])**2)
theta_p = sym.diff(theta,l)
my function, y is updated as follow: f(x_n) --> f(x_n + lambda*d_n) -- call this theta(lambda)
I've updated as above ('theta' function), and when printed to screen, it gives a numpy array:
array([-63.1124487914452*l + 2 + exp([1991.5905962264*(0.0316894691665188 - l)**2])],
dtype=object)
That is the equation I need, but now I want to differentiate it with respect to l, my lambda. But sympy doesn't work like this.
When I run
sym.diff(theta,l)
I get this output:
AttributeError: 'ImmutableDenseNDimArray' object has no attribute 'as_coeff_Mul'
Any ideas?
Try sym.diff(theta[0], l) and sym.diff(theta[1], l)
For some reason you end up with a ndarray containing objects that are sympy expressions. Print the type of each element to confirm.
Oh, there are nested ndarray expressions. You need to review what you are passing to theta.
You should end up with:
-63.1124487914452*l + 2 + exp(1991.5905962264*(0.0316894691665188 - l)**2)
instead of
array([-63.1124487914452*l + 2 + exp([1991.5905962264*(0.0316894691665188 - l)**2])],
dtype=object)
Replace x[0] and x[1] with symbols in theta, then diff, lambdify and evaluate with x[0] and x[1]
If xx (your unspecified x) is (2,1) array:
In [153]: xx = np.array([[1],[1]])
In [154]: grad1 = sym.lambdify([x1,x2],gy1)(xx[0],xx[1])
...: grad2 = sym.lambdify([x1,x2],gy2)(xx[0],xx[1])
...: d = np.array([-1*grad1,-1*grad2])
In [155]: d
Out[155]:
array([[-82.34214769],
[-46.17107385]])
In [156]: theta = 2*(xx[0]+l*d[0]) + 3*(xx[1]+l*d[1])**2 + sym.exp(2*(xx[0]+l*d[0])**2 + (xx[1]+l*d[1])
...: **2)
In [157]: theta
Out[157]:
array([-164.684295385501*l + 6395.30418038233*(0.0216585822397654 - l)**2 + 2 + exp([13560.4585733095*(0.0121444488396316 - l)**2 + 2131.76806012744*(0.0216585822397654 - l)**2])],
dtype=object)
If instead it is (2,) or a simple list
In [158]: xx = np.array([1,1]) # [1,1]
...
In [160]: d
Out[160]: array([-82.34214769, -46.17107385])
and theta is then a simple sympy expression, not an object array containing an expression. Then theta_p evaluates fine.
We can evaluate gy1 at specific x1,x2 with evalf instead of lambdify:
In [174]: xsub = {x1:1, x2:1}
In [175]: d = [-1*gy1.evalf(subs=xsub), -1*gy2.evalf(subs=xsub)]
In [176]: d
Out[176]: [-82.3421476927507, -46.1710738463753]

Harder equations (with Derivatives and Integrals) and ConditionSet in SymPy

I am planning to calculate b (Its also x on Xo axis) for which curve (function) length from 0 to x is equal 1.
By knowing: https://www.mathsisfun.com/calculus/arc-length.html
(integral from 0 to b) ∫ (1 + ((f’(x))^2)^(1/2) dx = 1
and that:
(integral from a to b) ∫ f(x)dx = F(b) - F(a)
We can calculate it by
1 - F(0) + F(b) = 0 , where this is now an Equation in terms of x, beacuse b as I said is x on Xo axis.
So now I tried it for f(x) = x**3 (full code will be below)
F(b) is equal to this monster: https://www.wolframalpha.com/input/?i=integral&assumption=%7B%22C%22%2C+%22integral%22%7D+-%3E+%7B%22Calculator%22%7D&assumption=%7B%22F%22%2C+%22Integral%22%2C+%22integrand%22%7D+-%3E%22%281+%2B+9x%5E4%29%5E%281%2F2%29%22
All I get from SymPy is ConditionSet but not number. Of course ConditionSet cannot be evauluated by evalf()
So here are my questions:
Did I make a mistake in math?
Is my code wrong and how to improve it?
Is SymPy enough to calculate this?
Did I misunderstand documentation?
Code:
from __future__ import division
import matplotlib.pyplot as plt
from sympy import *
x, y, z = symbols('x y z', real=True)
function1 = x**3
Antiderivative1 = integrate((1+(diff(function1))**2)**(1/2), x)
b = solveset(Eq(1 + Antiderivative1.subs(x, 0).evalf() - Antiderivative1, 0), x)
print(b)
Thats the output:
ConditionSet(x, Eq(x*hyper((-0.5, 1/4), (5/4,), 9*x**4*exp_polar(I*pi)) - 4.0*gamma(5/4)/gamma(1/4), 0), Complexes)
Thanks in advance and sorry for mistakes in grammar.
Note that you should use S(1)/2 or Rational(1, 2) (or sqrt) rather than 1/2 which will give you a float in Python. With that we have
In [16]: integrand = sqrt(1 + ((x**3).diff(x))**2)
In [17]: integrand
Out[17]:
__________
╱ 4
╲╱ 9⋅x + 1
In [18]: antiderivative = integrand.integrate(x)
In [19]: antiderivative
Out[19]:
┌─ ⎛-1/2, 1/4 │ 4 ⅈ⋅π⎞
x⋅Γ(1/4)⋅ ├─ ⎜ │ 9⋅x ⋅ℯ ⎟
2╵ 1 ⎝ 5/4 │ ⎠
─────────────────────────────────────
4⋅Γ(5/4)
While that isn't the same form as the result from Wolfram Alpha it could easily be the same function (up to an additive constant). From this result or the one on Wolfram Alpha I very much doubt that you will find an analytic solution (using SymPy or anything else).
You can however find a numerical solution. Unfortunately there is a bug in SymPy's lambdify function that means nsolve doesn't work with this function:
In [22]: nsolve(equation, x, 1)
...
NameError: name 'exp_polar' is not defined
We can do it ourselves with Newton steps though:
In [76]: f = equation.lhs
In [77]: fd = f.diff(x)
In [78]: newton = lambda xi: (xi - f.subs(x, xi)/fd.subs(x, xi)).evalf()
In [79]: xj = 1.0
In [80]: xj = newton(xj); print(xj)
0.826749667942050
In [81]: xj = newton(xj); print(xj)
0.791950624620750
In [82]: xj = newton(xj); print(xj)
0.790708415511451
In [83]: xj = newton(xj); print(xj)
0.790706893629886
In [84]: xj = newton(xj); print(xj)
0.790706893627605
In [85]: xj = newton(xj); print(xj)
0.790706893627605

Categories

Resources