How to sympify initial conditions for ODE in sympy? - sympy

I am passing initial conditions as string, to be used to solving an ODE in sympy.
It is a first order ode, so for example, lets take initial conditions as y(0):3 for example. From help
ics is the set of initial/boundary conditions for the differential
equation. It should be given in the form of {f(x0): x1,
f(x).diff(x).subs(x, x2): x3}
I need to pass this to sympy.dsolve. But sympify(ic) gives an error for some reason.
What other tricks to use to make this work? Here is MWE. First one shows it works without initial conditions being string (normal mode of operation)
from sympy import *
x = Symbol('x')
y = Function('y')
ode = Eq(Derivative(y(x),x),1+2*x)
sol = dsolve(ode,y(x),ics={y(0):3})
gives sol Eq(y(x), x**2 + x + 3)
Now the case when ics is string
from sympy import *
ic = "y(0):3"
x = Symbol('x')
y = Function('y')
ode = Eq(Derivative(y(x),x),1+2*x)
sol = dsolve(ode,y(x),ics={ sympify(ic) })
gives
SympifyError: Sympify of expression 'could not parse 'y(0):3'' failed,
because of exception being raised: SyntaxError: invalid syntax
(, line 1)
So looking at sympify webpage
sympify(a, locals=None, convert_xor=True, strict=False, rational=False, evaluate=None)
And tried changing different options as shown above, still the syntax error shows up.
I also tried
sol = dsolve(ode,y(x),ics= { eval(ic) } )
But this gives syntax error as well
Is there a trick to use to convert this initial conditions string to something sympy is happy with?
Python 4.7 with sympy 1.5
As temporary work around, currently I do this
from sympy import *
ic = "y(0):3"
ic = ic.split(":")
x = Symbol('x')
y = Function('y')
ode = Eq(Derivative(y(x),x),1+2*x)
sol = dsolve(ode,y(x),ics= {S(ic[0]):S(ic[1])} )
Which works. So the problem is with : initially, sympify (or S) do not handle : it seems.

You can use sympify('{y(0):3}').
I don't know what your actual goal is but I don't recommend parsing strings like this in general. The format for ICs is actually slightly awkward so that for a second order ODE it looks like:
ics = '{y(0):3, y(x).diff(x).subs(x, 0):1}'
If you're parsing a string then you can come up with a better syntax than that like
ics = "y(0)=3, y'(0)=1"
Also you should use parse_expr rather than converting strings with sympify or S:
https://docs.sympy.org/latest/modules/parsing.html#sympy.parsing.sympy_parser.parse_expr

Related

Commutator algebra in SymPy

I am solving a commutator algebra in SymPy with the Hamiltonian
from sympy import*
a=Operator("a")
ad=Dagger(a)
b=Operator("b")
bd=Dagger(b)
H= ad*a + bd*b
Is there any way I can define commutation relations such as $[a,a^\dagger]=1$,
$[b,b^\dagger]=1$ and
$[a,b]=0$ ?
I want it such that if I calculate $[a,ad*b]$ I get $b$.
There is a code in answer to one of the questions but it does not work in this case.
The function apply_ccr given by #m93a in here should work. You might want to try something like the following,
from sympy import *
from sympy.physics.quantum import *
com1 = Eq(Commutator(a, ad), 1)
com2 = Eq(Commutator(b, bd), 1)
com3 = Eq(Commutator(a, b), 0)
expr = (a*ad - ad*a) + (b*a + a*b) + (b*bd + bd*b) # example
print(expr) # βˆ’π‘Žβ€ π‘Ž+𝑏†𝑏+π‘Žπ‘Žβ€ +π‘Žπ‘+𝑏𝑏†+π‘π‘Ž
for com in [com1, com2, com3]:
expr = apply_ccr(expr, com)
print(expr) # 2+2𝑏†𝑏+2π‘Žπ‘
Note it doesn't seem to work if your expression contains commutators.

Unable to get the numerical result for a tanh function in Sympy

I am a python newbie. I am trying to get the two numerical results of the convection formula, but the best code I've created outputs a symbolic list containing the 'Lc' parameter, and not the expected numerical result. Anyone could give me a helping hand, please?
from sympy import var, tanh, solve
def convection ():
m = 0.9
Lc = var('Lc')
rend = 0.8
f = tanh(m*Lc)/(m*Lc)-rend
return solve(f,[m,Lc,rend],positive=True)
# Gotten : [(0.900000000000000, Lc, 1.11111111111111*tanh(0.9*Lc)/Lc)]
# Expected : [0.9, 0.986709867, 0.8] (or something like that)
Thank you in advance.
Your equation is:
In [33]: m = 0.9
In [34]: Lc = Symbol('Lc')
In [35]: rend = 0.8
In [36]: f = tanh(m*Lc)/(m*Lc)-rend
In [37]: f
Out[37]:
1.11111111111111β‹…tanh(0.9β‹…Lc)
-0.8 + ─────────────────────────────
Lc
The solve function is intended to find analytic solutions but that is often impossible for a transcendental equation such as this.
You are calling solve and asking it to solve for m and rend as well which is just confusing things. You should call it like:
In [38]: solve(f, Lc)
---------------------------------------------------------------------------
NotImplementedError
...
NotImplementedError: multiple generators [Lc, exp(Lc/10)]
No algorithms are implemented to solve equation -4/5 + 10*(exp(9*Lc/10) - exp(-9*Lc/10))/(9*Lc*(exp(9*Lc/10) + exp(-9*Lc/10)))
This fails because the transcendental equation can not be solved in explicit analytic form.
Instead if what you want is a numeric solution you can find that using nsolve:
In [41]: nsolve(f, Lc, 1)
Out[41]: 0.986683032622042
In [42]: nsolve(f, Lc, -1)
Out[42]: -0.986683032622042
Here we have to use an initial guess (e.g. 1 or -1) to seed the numeric solver but then we get a numeric answer.

Sympy - Simplify expression within domain

Can Sympy automatically simplify an expression that includes terms like this one:
cos(x)/(cos(x)**2)**(1/2)
which can be simplified to 1 in the domain that I am interested in 0 <= x <= pi/2 ?
(Examples of other terms that could be simplified in that domain: acos(cos(x)); sqrt(sin(x)**2); sqrt(cos(2*x) + 1); etc.)
If you know the functions that are in your expression (such as sin, cos and tan), you can do the following according to this stack overflow question:
from sympy import *
x = symbols("x", positive=True)
ex = cos(x)/(cos(x)**2)**(S(1)/2)
ex = refine(ex, Q.positive(sin(x)))
ex = refine(ex, Q.positive(cos(x)))
ex = refine(ex, Q.positive(tan(x)))
print(ex)
Note that Q.positive(x*(pi/2-x)) did not help in the process of simplification for trig functions even though this is exactly what you want in general.
But what if you might have crazy functions like polygamma? The following works for some arbitrary choices for ex according to my understanding.
It wouldn't be a problem if the expression was already generated before by SymPy, but if you are inputting the expression manually, I suggest using S(1)/2 or Rational(1, 2) to describe one half.
from sympy import *
# define everything as it would have come from previous code
# also define another variable y to be positive
x, y = symbols("x y", positive=True)
ex = cos(x)/(cos(x)**2)**(S(1)/2)
# If you can, always try to use S(1) or Rational(1, 2)
# if you are defining fractions.
# If it's already a pre-calculated variable in sympy,
# it will already understand it as a half, and you
# wouldn't have any problems.
# ex = cos(x)/(cos(x)**2)**(S(1)/2)
# if x = arctan(y) and both are positive,
# then we have implicitly that 0 < x < pi/2
ex = simplify(ex.replace(x, atan(y)))
# revert back to old variable x if x is still present
ex = simplify(ex.replace(y, tan(x)))
print(ex)
This trick can also be used to define other ranges. For example, if you wanted 1 < x, then you could have x = exp(y) where y = Symbol("y", positive=True).
I think subs() will also work instead of replace() but I just like to be forceful with substitutions, since SymPy can sometimes ignore the subs() command for some variable types like lists and stuff.
You can substitute for a symbol that has the assumptions you want:
In [27]: e = cos(x)/(cos(x)**2)**(S(1)/2) + cos(x)
In [28]: e
Out[28]:
cos(x)
cos(x) + ────────────
_________
β•± 2
β•²β•± cos (x)
In [29]: cosx = Dummy('cosx', positive=True)
In [30]: e.subs(cos(x), cosx).subs(cosx, cos(x))
Out[30]: cos(x) + 1

Sympy gives unexpected differentiation result when the input is a string

Why is the result of the differentiation not 2*x0 in the following code:
In [54]: import sympy
In [55]: x = [sympy.Symbol('x%d' % i, real=True) for i in range(3)]
In [56]: x
Out[56]: [x0, x1, x2]
In [57]: sympy.diff('x0*x0 + x1*x1 + x2*x2',x[0])
Out[57]: 0
First, the creation of multiple numbered symbols is simpler with
x = sympy.symbols('x0:3', real=True) # returns (x0, x1, x2)
Second, the SymPy function to turn a string into a SymPy expression is sympify. This function is called automatically when you provide input as a string; however, this gives you no control over the interpretation of the string, and "unexpected results" are likely.
In this case, SymPy is not sure that "x0" appearing in the string is the same as x0 you created earlier. After all, your x0 has the additional property of being real, and the symbol from the string has no such assumptions on it. It's Symbol('x0') vs Symbol('x0', real=True); not a match.
This is one of many reasons why throwing a string in a SymPy function is a bad idea. Use sympify, and read about its parameters which control the parsing of input. Specifically, locals parameter is a dictionary mapping pieces of the string to objects you already have in SymPy, precisely what is needed here.
locals = {'x{}'.format(i): x[i] for i in range(3)} # {'x0': x0, 'x1': x1, 'x2': x2}
expr = sympy.sympify('x0*x0 + x1*x1 + x2*x2', locals=locals)
Now you can differentiate expr with respect to any symbols and get expected results
[expr.diff(sym) for sym in x] # [2*x0, 2*x1, 2*x2]
(Another benefit of having an expression before trying diff is that you can invoke diff as a method of the expression, saving the trouble of typing sympy. prefix.)
In your declarations, you should use sympy.symbols that is the reference method (from the documentation and tutorial) to declare variables.
x = [sympy.symbols('x%d' % i, real=True) for i in range(3)]
On top of this, you must pick (from experimentations that I made) either a string in both arguments, as:
sympy.diff('x0*x0 + x1*x1 + x2*x2',str(x[0]))
or symbolic expressions on both sides:
sympy.diff(x[0]*x[0] + x[1]*x[1] + x[2]*x[2], x[0])

Selecting elements in numpy array using regular expressions

One may select elements in numpy arrays as follows
a = np.random.rand(100)
sel = a > 0.5 #select elements that are greater than 0.5
a[sel] = 0 #do something with the selection
b = np.array(list('abc abc abc'))
b[b==a] = 'A' #convert all the a's to A's
This property is used by the np.where function to retrive indices:
indices = np.where(a>0.9)
What I would like to do is to be able to use regular expressions in such element selection. For example, if I want to select elements from b above that match the [Aab] regexp, I need to write the following code:
regexp = '[Ab]'
selection = np.array([bool(re.search(regexp, element)) for element in b])
This looks too verbouse for me. Is there any shorter and more elegant way to do this?
There's some setup involved here, but unless numpy has some kind of direct support for regular expressions that I don't know about, then this is the most "numpytonic" solution. It tries to make iteration over the array more efficient than standard python iteration.
import numpy as np
import re
r = re.compile('[Ab]')
vmatch = np.vectorize(lambda x:bool(r.match(x)))
A = np.array(list('abc abc abc'))
sel = vmatch(A)