I am parsing a custom format like "{a} + {b}" into a sympy expression. I have that working successfully. Now, is there a way to convert that sympy expression back to the original string, assuming I have a dictionary mapping the free variable names and the corresponding string-in-braces representation?
Assuming the free variables from the above are "a" and "b", I want to do something like
str(expr.subs({'a': '{a}', 'b': '{b}'}))
but sympy doesn't seem to allow substituting in arbitrary strings like that.
You could subclass a codegenerator (e.g. StrPrinter), and overwrite the function that outputs the free variables. You can copy the original function from the sympy source and make some modifications.
Here is an example:
import sympy as sp
from sympy.printing import StrPrinter
class CustomStrPrinter(StrPrinter):
def _print_Symbol(self, expr):
return f'{{{expr.name}}}'
a, b = sp.symbols('a b')
expr = a + b
custom_strPrinter = CustomStrPrinter().doprint
print(custom_strPrinter(expr)) # {a} + {b}
Related
Given the following math function in form of a Python function:
import math
def f(x):
a = x - math.log(x)
b = x + math.log(x)
return a / x + b / math.log(x)
Is there any way that I can convert this function into a string like
expr = '(x - math.log(x)) / x + (x + math.log(x)) / math.log(x)'
so that when I want to call the function, I can simply use it by
func = lambda x: eval(expr)
print(func(3))
# 4.364513583657809
Note that I want to keep a and b in the original function. In reality, I have a lot more intermediate variables. Also, I am aware sympy could do similar tasks, but I would like to know if it is possible to convert the function to string, as it would be much more efficient to store.
Any suggestions?
Your function is already a string the moment you write it to a file!
If the function is valid Python, you can then just import it
from myfile import expr
print(expr(3)) # 4.364513583657809
WARNING Do not ever do this
If you want some incredibly evil logic for some reason, you can save your function directly with inspect.getsource(f) and then do something like this
>>> fn_body = """def f(x):
... a = x - math.log(x)
... b = x + math.log(x)
... return a / x + b / math.log(x)
... """
>>> eval(f'lambda {fn_body.split("(")[1].split(")")[0]}, _={exec(fn_body)}: {fn_body.split(" ", 1)[-1].split(")")[0]})')(3)
4.364513583657809
This works by finding the parts needed to call the function, evaluating the source as one of the args (to smuggle it into your namespace), and then building an anonymous function to call it
Further Caveats
not remotely maintainable
extremely fragile
will clobber or conflict with an existing function with the same name depending on use
you will still need to import math or whatever other libraries
won't work with default args without more pain
calling eval() first (before creating the lambda) will allow you to use inspect to get the signature (.signature()) and you can combine it with re and/or ast for a much robust parser, but a 1-liner seemed more exciting
manages to use both eval() and exec() for an extra helping of evil
You're probably looking for a symbolic equation solver!
Sympy's lambdify feature can do this for you!
>>> fn = sympy.lambdify("x", '(x - log(x)) / x + (x + log(x)) / log(x)')
>>> fn(x=3)
4.364513583657809
Caution: this also uses eval internally as #Joshua Voskamp warns about in a comment
I am trying to get a list of outputs from functions.
For example, let's say I define a function called 'compute' as below
def compute(a, b):
add = a + b
sub = a - b
return add, sub
What I want to do next is to create a new function that takes this 'compute' function as an argument and returns a list of outputs of the function, add and sub, as strings.
That is, if I name the function "output_list", I want the function output_list(compute) to return ['add', 'sub'].
It seems it is supposed to be simple, but I have trouble writing it.
What should the code look like?
This is not possible. The names of the local variables inside compute are not known outside of compute. In fact, the local variables very likely do not even exist at runtime at all.
Well, it might defeat the purpose but if you are the one who defines compute function, maybe you could do something like this:
from varname import nameof
def compute(a, b):
add = a + b
sub = a - b
compute.output_list = [ nameof(add), nameof(sub) ]
return add, sub
>>> compute.output_list
['add', 'sub']
Your question is a bit confusing, how do you want to put a called function "CONTAINS PARAMETERS" as a Parameter for a other function without mentioning the Parameters' values?? its a bit confusing... Now, do you want the Output to be a list of the variables as string or you want to list the variables' results in a list???
I will consider the best scenario that you want to list the results of variables as a list of values for another function..
Code Syntax
def compute(a, b):
add = a + b
sub = a - b
return [add, sub]
def another_function(lista= compute(3, 4)):
return lista
print(another_function())
OUTPUT
[7, -1]
[Program finished]
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())
I am taking a class and i'm confused. It would really help if you could guide me through the proccess of this and tell me what I am doing wrong. I have an error that has to do with the parentheses since theres nothing in them. I am a newbie so i'm sorry.
def FractionDivider(a,b,c,d):
n = ()
d = ()
n2 = ()
d2 = ()
print int(float(n)/d), int(float(n2)/d2)
return float (n)/d / (n2)/d2
Your function is taking in arguments a, b, c, and d, but you're not using them anywhere. You're instead defining four new variables. Try:
def FractionDivider(n, d, n2, d2):
and get rid of your empty parentheses bits, see if that does what you are trying to do.
you cannot declare a variable as you are doing n = () and then try to assign an integer or string to it.
n=() does not mean:
n equals nothing at the moment but i will assign a variable shortly.
() ---> Tuples https://docs.python.org/3/tutorial/datastructures.html
They are two examples of sequence data types (see Sequence Types —
list, tuple, range). Since Python is an evolving language, other
sequence data types may be added. There is also another standard
sequence data type: the tuple.
so within your function, if you want you varialbes to be assigned what is passed as an argument
for Ex:
def FractionDivider(a,b,c,d):
n = a
d = b
n2 = c
d2 = d
consider reading more on tuples from the above link
n=() is a valid python statement and there is no issue with that. However n=() is evaluating n to an empty tuple(). I believe that what you are trying to do is as follows.
def FractionDivider(a,b,c,d):
'''
Divides a fraction by another fraction...
'''
n = a #setting each individual parameter to a new name.
d = b #creating a pointer is often useful in order to preserve original data
n2 = c #but it is however not necessary in this function
d2 = d
return (float(n)/d) / (float(n2)/d2) #we return our math, Also order of operations exists here '''1/2/3/4 != (1/2)/(3/4)'''
print FractionDivider(1, 2, 3, 4) #here we print the result of our function call.
#indentation is extremely important in Python
here is a simpiler way of writing the same function
def FractionDivider_2(n,d,n2,d2):
return (float(n)/d) / (float(n2)/d2)
print FractionDivider_2(1,2,3,4)
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)