Sympy subs not outputting a substituted expression? - python

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.

Related

Multiprocessing : Use process_map with many arg function

I found this answer (https://stackoverflow.com/a/59905309/7462275) to display a progress bar very very simple to use. I would like to use this simple solution for functions that take many arguments.
Following, the above mentioned answer, I write this code that works :
from tqdm.contrib.concurrent import process_map
import time
def _foo(my_tuple):
my_number1, my_number2 = my_tuple
square = my_number1 * my_number2
time.sleep(1)
return square
r = process_map(_foo, [(i,j) for i,j in zip(range(0,30),range(100,130))],max_workers=mp.cpu_count())
But I wonder, if it is the correct solution (using a tuple to assign function variable) to do that. Thanks for answer

Consider a String a f-string in Python

Suppose I have
x = 3
s = "f'12{x}4'"
How to consider s as f-string to print 1234, like writing print(f'12{x}4')
when I print s, it prints it as it as: f'12{x}4'
Remve the double quotations that should fix the issue because the f in a f string needs to be outside the actuall string
You are missing two concepts.
The first one, is how you tell python your string is an f-string. The way to do this, is by adding the character 'f' before the first quotation mark:
f"this will be a f-string"
Whatever you have between {}, it will be a previously defined variable:
x = "something"
f"this will be {x}"
Assuming you ask this because you can not use actual f-strings, but also don't want to pass the parameters explicitly using format, maybe because you do not know which parameter are in the not-really-an-f-string, and also assuming you don't want to use eval, because, well, eval.
You could pass the variables in the locals or globals scope to format:
>>> x = 3
>>> s = '12{x}4'
>>> s.format(**globals())
'1234'
>>> s.format(**locals())
'1234'
Depending on where s is coming from (user input perhaps?) This might still be a bit risky, though, and it might be better to define a dict of "allowed" variables and use that in format. As with globals and locals, any unused variables do not matter.
>>> vars = {"x": x, "y": y, "z": z}
>>> s.format(**vars)
Note that this does not give you the full power of f-strings, though, which will also evaluate expressions. For instance, the above will not work for s = '12{x*x}4'.
You'd do this -
x = 3
s = f'12{x}4'
This can also work.
x = 3
s = "12{}4"
print(s.format(x))
Just remove extra commas
s = f'12{x}4'
x = 3
s = '12{}4'.format(x)
or
x = 3
print('12%s4' %x)

is something wrong with sympy solve method or latex parser?

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)

Partially unpack parameters in python

I know in Python we can unpack parameters from a tuple or list:
def add(x,y,z):
return x + y + z
xyz = (1,2,3)
s = add(*xyz)
But what is the proper way to accomplish something like this:
xy = (1,2)
s = add(*xy, 3)
SyntaxError: only named arguments may follow *expression
I can do this:
s = add(*xy + (3,))
but that looks ugly and badly readable, and if I have a few more variables in there it would be very messy.
So, is there a cleaner way to deal with such situation?
If you name your arguments; you can then proceed as you like:
>>> def foo(x=None,y=None,z=None):
... return x+y+z
...
>>> s = foo(*(1,2),z=3)
>>> s
6
Now if you do it like this, you can't override keyword arguments, so foo(*(1,2), y=3) will not work; but you can switch the order around as you like foo(z=3, *(1,2)).
I don't know that this is much cleaner, but since we're talking about partials..
from functools import partial
sum_ = partial(add,*xy)(3)
The pep for this has been proposed long back in 2007. You can take a look at it here - http://www.python.org/dev/peps/pep-3132
Although it might not come in py3.4 but it is certainly accepted by Guido & is proposed to come in some python 3 release.
sum = add(3, *xy)
Hope this will do.
The prototype for a method declaration in python is:
def method1(arg1,arg2, *args, **kwargs):
....your code.....

Force function argument to be a string

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)

Categories