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])
Related
I have a question similar to this one: How to substitute multiple symbols in an expression in sympy? but in reverse.
I have a sympy expression with numerical values and symbols alike. I would like to substitute all numerical values with symbolic constants. I appreciate that such query is uncommon for sympy. What can I try next?
For example, I have:
-0.5967695*sin(0.15280747*x0 + 0.89256966) + 0.5967695*sin(sin(0.004289882*x0 - 1.5390939)) and would like to replace all numbers with a, b, c etc. ideally in a batch type of way.
The goal is to then apply trig identities to simplify the expression.
I'm not sure if there is already such a function. If there is not, it's quite easy to build one. For example:
import string
def num2symbols(expr):
# wild symbol to select all numbers
w = Wild("w", properties=[lambda t: isinstance(t, Number)])
# extract the numbers from the expression
n = expr.find(w)
# get a lowercase alphabet
alphabet = list(string.ascii_lowercase)
# create a symbol for each number
s = symbols(" ".join(alphabet[:len(n)]))
# create a dictionary mapping a number to a symbol
d = {k: v for k, v in zip(n, s)}
return d, expr.subs(d)
x0 = symbols("x0")
expr = -0.5967695*sin(0.15280747*x0 + 0.89256966) + 0.5967695*sin(sin(0.004289882*x0 - 1.5390939))
d, new_expr = num2symbols(expr)
print(new_expr)
# out: b*sin(c + d*x0) - b*sin(sin(a + f*x0))
print(d):
# {-1.53909390000000: a, -0.596769500000000: b, 0.892569660000000: c, 0.152807470000000: d, 0.596769500000000: e, 0.00428988200000000: f}
I feel like dict.setdefault was made for this purpose in Python :-)
>>> c = numbered_symbols('c',cls=Dummy)
>>> d = {}
>>> econ = expr.replace(lambda x:x.is_Float, lambda x: sign(x)*d.setdefault(abs(x),next(c)))
>>> undo = {v:k for k,v in d.items()}
Do what you want with econ and when done (after saving results to econ)
>>> econ.xreplace(undo) == expr
True
(But if you change econ the exact equivalence may no longer hold.) This uses abs to store symbols so if the expression has constants that differ by a sign they will appear in econ with +/-ci instead of ci and cj.
I am trying to solve differential equation y'=cot(x) using Sympy:
x = symbols("x")
y = Function("y")(x)
dsolve(diff(y, x) - cot(x))
It gives me solution with logarithm: y(x) = C1 + log(sin(x)).
How I can replace in this expression log by combination log + abs: log(sin(x)) -> log(abs(sin(x)))? I can do it manually, but it's not very convenient. Is there any way to do it with SymPy tools (something like subs command)?
Note that the abs isn't actually needed: the integration constant C1 can include I*pi. The form without abs is valid for all complex x whereas the abs form is only valid for real x.
In any case, you can do it like this:
In [6]: sol
Out[6]: y(x) = C₁ + log(sin(x))
In [7]: sol.replace(log, lambda e: log(abs(e)))
Out[7]: y(x) = C₁ + log(│sin(x)│)
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
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
I have been working on some integrations and even though the system is working, it takes much more time to work than it should.
The problem is that the expressions are many pages, and even though they are 3 variables only, sy.simplify just crashes the Kernel after 4 hours or so.
Is there a way to make such lengthy expressions more compact?
EDIT:
Trying to recreate a test expression, using cse. I can't really substitute the symbols to make a final expression, equal to the 1st one
sy.var('a:c x')
testexp = sp.log(x)+a*(0.5*x)**2+(b*(0.5*x)**2+b+sp.log(x))/c
r, e = sy.cse(testexp)
FinalFunction = sy.lambdify(r[0:][0]+(a,b,c,x),e[0])
Points = sy.lambdify((a,b,c,x),r[0:][1])
FinalFunction(Points(1,1,1,1),1,1,1,1)
>>>NameError: name 'x1' is not defined
cse(expr) is sometimes a way to get a more compact representation since repeated subexpressions can be replaced with a single symbol. cse returns a list of repeated expressions and a list of expressions (a singleton if you only passed a single expression):
>>> from sympy import solve
>>> var('a:c x');solve(a*x**2+b*x+c, x)
(a, b, c, x)
[(-b + sqrt(-4*a*c + b**2))/(2*a), -(b + sqrt(-4*a*c + b**2))/(2*a)]
>>> r, e = cse(_)
>>> for i in r: pprint(Eq(*i))
...
_____________
╱ 2
x₀ = ╲╱ -4⋅a⋅c + b
1
x₁ = ───
2⋅a
>>> for i in e: pprint(i)
...
x₁⋅(-b + x₀)
-x₁⋅(b + x₀)
You are still going to have long expressions but they will be represented more compactly (and more efficiently for computatation) if cse is able to identify repeated subexpressions.
To use this in SymPy you can create two Lambdas: one to translate the variables into the replacement values and the other to use those values:
>>> v = (a,b,c,x)
>>> Pts = Lambda(v, tuple([i[1] for i in r]+list(v)))
>>> Pts(1,2,3,4)
(2*sqrt(2)*I, 1/2, 1, 2, 3, 4)
>>> Func = Lambda(tuple([i[0] for i in r]+list(v)), tuple(e))
>>> Func(*Pts(1,2,3,4))
(-1 + sqrt(2)*I, -1 - sqrt(2)*I)