I am trying to convert latex expression to sympy form and then solve it.
When I feed the output of the parser(or converter actually?) to solve method, it finds no solution. However, if I manually enter the parser generated expression, it finds the roots successfully. What is wrong with parse_latex ( most probably ) or solve method?
Thanks in advance. Here is the code sample you can try:
from sympy import*
from sympy.parsing.latex import*
x = Symbol("x", real=True)
sym_eqn = parse_latex("|x-2|-1")
print sym_eqn # Abs(x - 2) - 1
print type(sym_eqn) # <class 'sympy.core.add.Add'>
print type(Abs(x - 2) - 1) # <class 'sympy.core.add.Add'>
print solve(Abs(x-2)-1) # [1,3], which is ok
#print solve(sym_eqn) # NotImplementedError: solving Abs(x - 2) when the argument is not real or imaginary.
print solve(sym_eqn,x) # []
The root issue here is whether or not your symbol 'x' has an attribute "real" set to True, or not.
Consider the following two symbols:
a = Symbol('x',real=True)
b = Symbol('x')
a and b are not of the same type and in fact a==b is False.
What happens when you execute
sym_eqn = parse_latex("|x-2|-1")
is that that sym_eqn is now an expression that contains a Symbol that does not have the attribute real set to True which is required to run solve on it.
Having understood this, the question is now how to get parse_latex to return an expression that would contain a Symbol that is real?
The only way I found is to write a function that recursively traverses the expression's tree and rebuilds a copy of it such that the result is the same, except all Symbols are now real.
def rewrite_expr_real(expr):
res_list = []
if isinstance(expr,Symbol):
return Symbol(str(expr),real=True)
if not expr.args:
return expr
for a in expr.args:
res_list.append(rewrite_expr_real(a))
return expr.func(*tuple(res_list))
Now,
if you rewrite your code as follows:
sym_eqn = rewrite_expr_real(parse_latex("|x-2|-1"))
The rest of your code will work as you expect it.
Yakov's answer is on point, but I'd like to offer a code snippet that converts all symbols to "real" in a single substitution.
from sympy import symbols
expr = expr.subs((str(symbol), symbols(str(symbol), real=True))
for symbol in expr.free_symbols)
Related
I have a simple differentiation function
def differentiate(func, num) -> float:
x = num
h = 0.000000001
der = (func(x+h)-func(x))/h
return round(der,4)
print(differentiate(lambda x: x+5,10))
Which gives the expected output of 1.0 But I want to make the func argument such that it only needs the expression to be in the form of a string. For example:
print(differentiate('x+5', 10))
Is this possible to do? Preferably without the help of modules.
It depends whether x is always your variable. If it is the case, then you can use the eval function of python which parses your string and evaluate it as a python expression:
def differentiate(func_x, num) -> float:
x = num
h = 0.000000001
func = lambda x: eval(func_x)
der = (func(x+h)-func(x))/h
return round(der,4)
print(differentiate('x+5', 10))
>>> 1.0
Edit:
As Serge Ballesta pointed out below, the eval function may have security issues as it allows uncontrolled execution at run time, i.e. it will execute any piece of code given in input, so only use it if you can trust the input of your function.
Is there a way to create a python function from a string? For example, I have the following expression as a string:
dSdt = "-1* beta * s * i"
I've found a way to tokenize it:
>>> import re
>>> re.findall(r"(\b\w*[\.]?\w+\b|[\(\)\+\*\-\/])", dSdt)
['-', '1', '*', 'beta', '*', 's', '*', 'i']
And now I want to (somehow - and this is the part I don't know) convert it to something with the same behavior as:
def dSdt(beta, s, i):
return -1*beta*s*i
I've thought about something like eval(dSdt), but I want it to be more general (the parameters beta, s and i would have to be known to exist ahead of time).
Some close requests have linked to this question for evaluating a mathematical expression in a string. This is not quite the same as this question, as I'm looking to define a function from that string.
One way, using exec to define a new function from string
expr = "-1* beta * s * i"
name = "dSdt"
params = ["beta","s","i"] # Figure out how to build this array from expression
param_str = ",".join(params)
exec (f"def {name}({param_str}): return {expr}")
dSdt(1,2,3)
Out[]: -6
If you don't care about defining a reusable function can also use eval with the global object argument.
expr = "-1* beta * s * i"
param= {"beta":1, "s":2, "i":3} # Find way to build this.
eval(expr,param)
Out[]: -6
This is exactly what a compiler or interpreter does: translate from one language syntax to another. The main question here is when do you want to be able to execute the resulting function? Is it enough to write the function to a file to be used later by some other program? Or do you need to use it immediately by the parser in some way? For both situations, I would write a parser that creates an Abstract Syntax Tree from the tokens. This means you will need to make a more complex tokenizer that labels each token as an "operator", "number", or "variable". Usually this is done by writing a single regular expression for each type of token.
Then you can build a parser that consumes each token one at a time and builds an Abstract Syntax Tree that represents the expression. There is plenty of material online explaining how to do this, so I suggest some googling. You might also want to look for libraries that help with this.
Finally, you can traverse the AST and either write out the corresponding Python syntax to a file or evaluate the expression with some input for values of variables.
You're talking about how you cannot know the arguments beforehand - that's where *args and **kwargs are very useful!
I like this idea of yours and the tokenize function you made works pretty good.
I made a very general function for you that can handle any expression as long as you add the operators and functions you want to use inside the 'ignore' list. Then you simply need to add the variable values in the order that they appear in the expression.
import re
from math import sqrt
ignore = ["+", "-", "*", "/", "(", ")", "sqrt"]
def tokenize(expression):
return re.findall(r"(\b\w*[\.]?\w+\b|[\(\)\+\*\-\/])", expression)
def calculate(expression, *args):
seenArgs = {}
newTokens = []
tokens = tokenize(expression)
for token in tokens:
try:
float(token)
except ValueError:
tokenIsFloat = False
else:
tokenIsFloat = True
if token in ignore or tokenIsFloat:
newTokens.append(token)
else:
if token not in seenArgs:
seenArgs[token] = str(args[len(seenArgs)])
newTokens.append(seenArgs[token])
return eval("".join(newTokens))
print(calculate("-1* beta * s * i", 1, 2, 3))
print(calculate("5.5 * x * x", 3))
print(calculate("sqrt(x) * y", 9, 2))
Results in:
-6
49.5
6.0
I am using sympy to differentiate a function in python. After differentiating the function, I would like to add in the numerical value of the variable that I differentiated with. However, using .subs() does not return a different answer. Does anyone have an idea s to what my issue is?
Code:
CA1 = CA0 * sympy.exp(-(A1*sympy.exp(-E1/(R*T)))*t)
dCa_dA12 = diff(CA1, A1)
print("No substitution:", dCa_dA12)
dCa_1 = dCa_dA12.subs(A1, theta[0])
print("Substitution:", dCa_1)
Output:
I had the same problem and was tinkering around a bit:
This works:
>>> sympify("k").evalf(subs={"k":1})
1.00000000000000
This doesn't work:
>>> sympify("k+x").evalf(subs={"k":1})
k + x
This again works:
>>> sympify("k+x").evalf(subs={"k":1, "x":2})
3.00000000000000
So it seems the substitution doesn't work if the result is not a number. Strangely, this only applies to the subs part:
>>> sympify("2/3*x")
2*x/3
>>> sympify("2/3*x").evalf()
0.666666666666667*x
This looks like a bug to me. At least, it should be documented properly.
I am writing a program that requires the user to enter an expression. This expression is entered as a string and converted to a Sympy expression using parse_expr. I then need to take the partial derivative of that expression that the user entered. However, diff is returning 0 with every expression I am testing.
For example if the user enters a*exp(-b*(x-c)**(2)), using the following code, diff returns 0 when it should (as far as I know about diff) return 2*a*b*(c - x)*exp(-b*(x - c)**2) when taking the partial derivative with respect to x:
a, b, c, x = symbols('a b c x', real=True)
str_expr = "a*exp(-b*(x-c)**(2))"
parsed_expr = parse_expr(str_expr)
result = diff(parsed_expr, x)
print(result) # prints 0
What am I doing wrong?
Bottom line: use parse_expr(str_expr,locals()).
Add global_dict=<dict of allowed entities to use>, too, if the expression may use any entities not imported into the local namespace and not accessible with the default from sympy import *.
According to Calculus — SymPy Tutorial - SymPy 1.0.1.dev documentation, you type the symbolic expression into the diff() argument as-is. Due to the fact that the letters are Symbol objects (with overridden operators), Python is tricked into constructing the SymPy object corresponding to the expression as it evaluates the argument!
Thus, if you have it as a string, you eval it to trigger the same behaviour:
<...>
>>> s="a*exp(-b*(x-c)**(2))"
>>> diff(eval(s), x)
−ab(−2c+2x)e−b(−c+x)2
But eval is a security hazard if used with untrusted input because it accepts arbitrary Python code.
This is where replacements like parse_expr come into play. However, due to the way expressions are parsed, described above, it needs access to the external entities used in the expression - like the Symbol objects for variables and function objects for the named functions used - through the local_dict and global_dict arguments.
Otherwise, it creates the Symbol objects on the fly. Which means, the Symbol object it has created for x in the expression is different from the variable x! No wonder that the derivative over it is 0!
<...>
>>> ps=parse_expr(s)
>>> ps.free_symbols
{a,b,c,x}
>>> x in _
False
>>> diff(ps,x)
0
>>> ps=parse_expr(s,locals())
>>> x in ps.free_symbols
True
>>> diff(ps,x)
-ab(−2c+2x)e−b(−c+x)2
Work is ongoing to make sympify safer than eval. Better to use something like the following:
from sympy import *
var ('a b c x')
str_expr = "a*exp(-b*(x-c)**(2))"
parsed_expr = sympify(str_expr)
result = diff(parsed_expr, x)
print(result)
Result:
-a*b*(-2*c + 2*x)*exp(-b*(-c + x)**2)
Replace a, b, c, x = symbols('a b c x', real=True) with:
a = Symbol('a')
b = Symbol('b')
c = Symbol('c')
x = Symbol('x')
Symbols with different assumptions compare unequal:
>>> Symbol('x') == Symbol('x', real=True)
False
When you use sympify or parse_expr, it parses unknown variables as symbols without assumptions. In your case, this creates Symbol('x'), which is considered distinct from the Symbol('x', real=True) you already created.
The solution is to either remove the assumptions, or include the locals() dictionary when you parse, so that it recognizes the name x as being the Symbol('x', real=True) that you already defined, like
parse_expr(str_expr,locals())
or
sympify(str_expr, locals())
So, my code is like this:
def func(s,x):
return eval(s.replace('x',x)
#Example:
>> func('x**2 + 3*x',1)
4
The first argument of the function func must be a string because the function eval accepts only string or code objects. However, I'd like to use this function in a kind of calculator, where the user types for example 2 + sin(2*pi-0.15) + func(1.8*x-32,273) and gets the answer of the expression, and it's annoying always to have to write the quotes before in the expression inside func().
Is there a way to make python understands the s argument is always a string, even when it's not between quotes?
No, it is not possible. You can't intercept the Python interpreter before it parses and evaluates 1.8*x-32.
Using eval as a glorified calculator is a highly questionable idea. The user could pass in all kinds of malicious Python code. If you're going to do it, you should provide as minimal an environment as possible for the code to run in. Pass in your own globals dict containing only the variables the user is allowed to reference.
return eval(s, {'x': x})
Besides being safer, this is also a better way to substitute x into the expression.
You could have it handle both cases:
def func(s, x=0):
if isinstance(s, basestring):
# x is in the scope, so you don't need to replace the string
return eval(s)
else:
return s
And the output:
>>> from math import *
>>> func('2 + sin(2*pi-0.15) + func(1.8*x-32,273)')
-30.1494381324736
>>> func('x**2 + 3*x', 1)
4
Caution: eval can do more than just add numbers. I can type __import__('os').system('rm /your/homework.doc') and your calculator will delete your homework.
In a word: no, if I understand you.
In a few more, you can sort of get around the problem by making x be a special object. This is how the Python math library SymPy works. For example:
>>> from sympy import Symbol
>>> x = Symbol('x')
>>> x**2+3*x
x**2 + 3*x
>>> (x**2+3*x).subs(x,1)
4
There's even a handy function to turn strings into sympy objects:
>>> from sympy import sympify, pi
>>> sympify("x**2 - sin(x)")
x**2 - sin(x)
>>> _.subs(x, pi)
pi**2
All the warnings about untrusted user input hold. [I'm too lazy to check whether or not eval or exec is used on the sympify code path, and as they say, every weapon is loaded, even the unloaded ones.]
You can write an interpreter:
import code
def readfunc(prompt):
raw = input(prompt)
if raw.count(',')!=1:
print('Bad expression: {}'.format(raw))
return ''
s, x = raw.split(',')
return '''x={}; {}'''.format(x, s)
code.interact('Calc 0.1', readfunc)