How can I dynamically generate a python symbol statement? - python

I am trying to write a routine that normalizes (rewrites) a mathematical equation that may have more than one symbol on the LHS so that it only has one.
The following code illustrates what I want to do
Assume I have an equation
ln(x)-ln(x1)= -(a+by)
I want to solve for x or return
x=x1*exp(-a+by)
Using sympy I can do the following
from sympy import *
formula=' log(x)-log(x1) =-(a+b*y)'
lhs,rhs=formula.split('=',1)
x,x_1,y,a,b,y=symbols('x x_1 y a b y')
r=sympy.solve(eval(lhs)-eval(rhs),x)
r
==>
Output: [x1*exp(-a - b*y)]
I am trying to automate this for a range of input lines as follows
from sympy import *
import re
# eventually to be read ina loop from a file
formula="DLOG(SAUMMCREDBISCN/SAUNECONPRVTXN) =-0.142368233181-0.22796245228*(LOG(SAUMMCREDBISCN(-1)/SAUNECONPRVTXN(-1))+0.2*((SAUMMLOANINTRCN(-1)-SAUINTR(-1))/100)-LOG(SAUNYGDPMKTPKN(-1)))+0.576050997065*SAUNYGDPGAP_/100"
#try to convert formula into a string containing just the synbols
sym1=formula.replace("*"," ")
sym1=sym1.replace("DLOG"," ")
sym1=sym1.replace("LOG"," ")
sym1=sym1.replace("EXP"," ")
sym1=sym1.replace("RECODE"," ")
sym1=re.sub('[()/+-\=]',' ',sym1)
sym1=re.sub(' +',' ',sym1)
#This logic works for this particular formula
sym1
#now generate a string that has, instead of spaces between symbols
ss2=sym1.replace(' ',',')
#This is the part that does not work I want to generate a command that effectively says
#symbol,symbol2,..,symboln=symbols('symbol1 symbol2 ... symboln')
#tried this but it fails
eval(ss2)=symbols(sym1)
Generates the result
eval(ss2)=symbols(sym1)
^
SyntaxError: can't assign to function call
Any help for this py noob, would be greatly appreciated.

var('a b c') will inject symbol name 'a', 'b', 'c' into the namespace but perhaps #Blorgbeard is asking about lists or dicts because instead of creating many symbols you could put the symbols in a dictionary and then access them by name:
>>> formula=' log(x)-log(x1) =-(a+b*y)'
>>> eq = Eq(*map(S, formula.split('=', 1)))
>>> v = dict([(i.name, i) for i in eq.free_symbols]); v
{'y': y, 'b': b, 'x': x, 'x1': x1, 'a': a}
>>> solve(eq, v['x'])
[x1*exp(-a - b*y)]
So it is not actually necessary to use eval or to have a variable matching a symbol name: S can convert a string to an expression and free_symbols can identify which symbols are present. Putting them in a dictionary with keys being the Symbol name allows them to be retrieved from the dictionary with a string.

I think what you want to generate a string from these two statements and then you can eval that string
str_eq = f'{ss2} = {symbols(sym1)}'
print(str_eq)
>>' ,SAUMMCREDBISCN,SAUNECONPRVTXN,SAUMMCREDBISCN,SAUNECONPRVTXN,SAUMMLOANINTRCN,SAUINTR,SAUNYGDPMKTPKN,SAUNYGDPGAP_, = (SAUMMCREDBISCN, SAUNECONPRVTXN, SAUMMCREDBISCN, SAUNECONPRVTXN, SAUMMLOANINTRCN, SAUINTR, SAUNYGDPMKTPKN, SAUNYGDPGAP_)'
The first line says - give me a string but run the python code between the {} before returning it. For instance
print(f'Adding two numbers: (2+3) = {2 + 3}')
>> Adding two numbers: (2+3) = 5

First, maybe this could help make your code a bit denser.
If I understand correctly, you're trying to assign a variable using a list of names. You could use vars()[x]=:
import re
from sympy import symbols
symbol_names = re.findall("SAU[A-Z_]+",formula)
symbol_objs = symbols(symbol_names)
for sym_name,sym_obj in zip(symbol_names,symbol_objs):
vars()[sym] = sym_obj

A colleague of mine (Ib Hansen) gave me a very nice and elegant solution to the original problem (how to solve for the complicated expression) that by-passed the string manipulation solution that my original question was struggling with and which most answers addressed.
His solution is to use sympify and solve from sympy
from sympy import sympify, solve
import re
def normalize(var,eq,simplify=False,manual=False):
'''normalize an equation with respect to var using sympy'''
lhs, rhs = eq.split('=')
kat = sympify(f'Eq({lhs},{rhs})')
var_sym = sympify(var)
out = str(solve(kat, var_sym,simplify=simplify,manual=manual))[1:-1]
return f'{var} = {out}'
This works perfectly
from sympy import sympify, solve
import re
def norm(var,eq,simplify=False,manual=False):
'''normalize an equation with respect to var using sympy'''
lhs, rhs = eq.split('=')
kat = sympify(f'Eq({lhs},{rhs})')
var_sym = sympify(var)
out = str(solve(kat, var_sym,simplify=simplify,manual=manual))[1:-1] # simplify takes for ever
return f'{var} = {out}'
``` Re write equation spelling out the dlog (delta log) function that python does no know, and putting log into lowe case '''
test8=norm('saummcredbiscn','log(saummcredbiscn/sauneconprvtxn) -log(saummcredbiscn(-1)/sauneconprvtxn(-1)) =-0.142368233181-0.22796245228*(log(saummcredbiscn(-1)/sauneconprvtxn(-1))+0.2*((saummloanintrcn(-1)-sauintr(-1))/100)-log(saunygdpmktpkn(-1)))+0.576050997065*saunygdpgap_/100')
print (test8)
with result
saummcredbiscn = 0.867301828366361*sauneconprvtxn*(saummcredbiscn(-1.0)/sauneconprvtxn(-1.0))**(19300938693/25000000000)*saunygdpmktpkn(-1.0)**(5699061307/25000000000)*exp(0.00576050997065*saunygdpgap_ + 0.00045592490456*sauintr(-1.0) - 0.00045592490456*saummloanintrcn(-1.0)

Related

Convert string of a named expression in sympy to the expression itself

Description of the practical problem:
I have defined many expression using sympy, as in
import sympy as sp
a, b = sp.symbols('a,b', real=True, positive=True)
Xcharles_YclassA_Zregion1 = 1.01 * a**1.01 * b**0.99
Xbob_YclassA_Zregion1 = 1.009999 * a**1.01 * b**0.99
Xbob_YclassA_Zregion2 = 1.009999 * a**1.01 * b**0.99000000001
...
So I have used the names of the expressions to describe options (e.g., charles, bob) within categories (e.g., X).
Now I want a function that takes two strings (e.g., 'Xcharles_YclassA_Zregion1' and 'Xbob_YclassA_Zregion1') and returns its simplified ratio (in this example, 1.00000099009999), so I can quickly check "how different" they are, in terms of result, not in terms of how they are written.
E.g., 2*a and a*2 are the same for my objective.
How can I achieve this?
Notes:
The expressions in the example are hardcoded for the sake of simplicity. But in my actual case they come from a sequence of many other expressions and operations.
Not all combinations of options for all categories would exist. E.g., Xcharles_YclassA_Zregion2 may not exist. Actually, if I were to write a table for existing expression names, it would be sparsely filled.
I guess rewriting my code using dict to store the table might solve my problem. But I would have to modify a lot of code for that.
Besides the practical aspects of my objective, I don't know if there is any formal difference between Symbol (which is a specific class) and expression. From the sources I read (e.g., this) I did not arrive to a conclusion. This understanding may help in solving the question.
TL;DR - What I tried
I aimed at something like
def verify_ratio(vstr1, vstr2):
"""Compare the result of two different computations of the same quantity"""
ratio = sp.N(sp.parsing.sympy_parser.parse_expr(vstr1)) / sp.parsing.sympy_parser.parse_expr(vstr2)
print(vstr1 + ' / ' + vstr2, '=', sp.N(ratio))
return
This did not work.
Code below shows why
import sympy as sp
a, b = sp.symbols('a,b', real=True, positive=True)
expr2 = 1.01 * a**1.01 * b**0.99
print(type(expr2), '->', expr2)
expr2b = sp.parsing.sympy_parser.parse_expr('expr2')
print(type(expr2b), '->', expr2b)
expr2c = sp.N(sp.parsing.sympy_parser.parse_expr('expr2'))
print(type(expr2c), '->', expr2c)
#print(sp.N(sp.parsing.sympy_parser.parse_expr('expr2')))
expr2d = sp.sympify('expr2')
print(type(expr2d), '->', expr2d)
with output
<class 'sympy.core.mul.Mul'> -> 1.01*a**1.01*b**0.99
<class 'sympy.core.symbol.Symbol'> -> expr2
<class 'sympy.core.symbol.Symbol'> -> expr2
<class 'sympy.core.symbol.Symbol'> -> expr2
I need something that takes the string 'expr2' and returns the expression 1.01 * a**1.01 * b**0.99.
None of my attempts achieved the objective.
Questions or links which did not help (at least for me):
From string to sympy expression
https://docs.sympy.org/latest/tutorials/intro-tutorial/basic_operations.html
https://docs.sympy.org/latest/modules/parsing.html
https://docs.sympy.org/latest/modules/core.html#sympy.core.sympify.sympify
https://docs.sympy.org/latest/tutorials/intro-tutorial/manipulation.html
If, when parsing, you want to use the expression that has been mapped to a variable you have to pass the dictionary that python uses to keep track of those mappings, i.e. locals()
>>> from sympy.abc import x
>>> from sympy import sympify, parse_expr
>>> y = x + 2
>>> sympify('y')
y
>>> sympify('y', locals=locals())
x + 2
>>> parse_expr('y', local_dict=locals())
x + 2
As suggested by Oscar Benjamin from the Sympy Google group, eval does the job
def verify_ratio(vstr1, vstr2):
"""Compare the result of two different computations of the same quantity"""
print(vstr1 + ' / ' + vstr2, '=', eval(vstr1 + ' / ' + vstr2))
return
What shows this would work is
>>> import sys
>>> import sympy as sp
>>> a, b = sp.symbols('a,b', real=True, positive=True)
>>> a is eval('a')
True
In my case, all expressions and symbols I am using in vstr1 and vstr2 are global.
If nesting within other functions, I might need to pass further parameters to verify_ratio, as in the solution by smichr.

I want solve this Equations in python

I'm work with Python
it's simple (1.1*x)+(b+(b*0.1))=a this equation is what I want to solve.
I'm so newbie in this world so I having a problem with it
"a" and "b" is come with
int(input('factor a : '))
int(input('factor b : '))
How can I script this to make an calculator
I don't know what values ​​you would set the X to but you would just add the part of the equation and assemble it already in this code.
import math
a = int(input('factor a : '))
b = int(input('factor b : '))
print((b+(b*0.1))/a)
Depending on the kind of project that you have in mind you can use symbolic mathematics program to gather flexibility.
Here an example with sympy and a live shell to test it without installations.
from sympy import symbols, Eq, solve
# declare the symbols
x, a, b = symbols('x a b')
# set up the equation
eq = Eq((1.1*x)+(b+(b*0.1)), a)
# solve it (for hard ones there are several types of solvers)
sol = solve(eq, x)
# fix the free variables
extra_pars = {a.name: input('a:'), b.name: input('b')}
# replace into the expression
new_sol = sol[0].subs(extra_pars)
print(new_sol)

Saving an image of an expression displayed with Sympy?

I'm relatively new to Sympy and had a lot of trouble with the information that I was able to scavenge on this site. My main goal is basically to take a string, representing some mathematical expression, and then save an image of that expression but in a cleaner form.
So for example, if this is the expression string:
"2**x+(3-(4*9))"
I want it to display like this
cleaner image.
This is currently the code that I have written in order to achieve this, based off of what I was able to read on StackExchange:
from matplotlib import pylab
from sympy.parsing.sympy_parser import parse_expr
from sympy.plotting import plot
from sympy.printing.preview import preview
class MathString:
def __init__(self, expression_str: str):
self.expression_str = expression_str
#property
def expression(self):
return parse_expr(self.expression_str)
def plot_expression(self):
return plot(self.expression)
def save_plot(self):
self.plot_expression().saveimage("imagePath",
format='png')
And then using a main function:
def main():
test_expression = '2**x+(3-(4*9))'
test = MathString(test_expression)
test.save_plot()
main()
However, when I run main(), it just sends me an actual graphical plot of the equation I provided. I've tried multiple other solutions but the errors ranged from my environment not supporting LaTeX to the fact that I am passing trying to pass the expression as a string.
Please help! I'm super stuck and do not understand what I am doing wrong! Given a certain address path where I can store my images, how can I save an image of the displayed expression using Sympy/MatPlotLib/whatever other libraries I may need?
The program in your question does not convert the expression from
string format to the sympy internal format. See below for examples.
Also, sympy has capabilities to detect what works best in your
environment. Running the following program in Spyder 5.0 with an
iPython 7.22 terminal, I get the output in Unicode format.
from sympy import *
my_names = 'x'
x = symbols(','.join(my_names))
ns1 = {'x': x}
my_symbols = tuple(ns1.values())
es1 = '2**x+(3-(4*9))'
e1 = sympify(es1, locals=ns1)
e2 = sympify(es1, locals=ns1, evaluate=False)
print(f"String {es1} with symbols {my_symbols}",
f"\n\tmakes expression {e1}",
f"\n\tor expression {e2}")
# print(simplify(e1))
init_printing()
pprint(e2)
Output (in Unicode):
# String 2**x+(3-(4*9)) with symbols (x,)
# makes expression 2**x - 33
# or expression 2**x - 4*9 + 3
# x
# 2 - 4⋅9 + 3

lambdify a sympy expression that contains a Derivative of UndefinedFunction

I have several expressions of an undefined function some of which contain the corresponding (undefined) derivatives of that function. Both the function and its derivatives exist only as numerical data. I want to make functions out of my expressions and then call that function with the corresponding numerical data to numerically compute the expression. Unfortunately I have run into a problem with lambdify.
Consider the following simplified example:
import sympy
import numpy
# define a parameter and an unknown function on said parameter
t = sympy.Symbol('t')
s = sympy.Function('s')(t)
# a "normal" expression
a = t*s**2
print(a)
#OUT: t*s(t)**2
# an expression which contains a derivative
b = a.diff(t)
print(b)
#OUT: 2*t*s(t)*Derivative(s(t), t) + s(t)**2
# generate an arbitrary numerical input
# for demo purposes lets assume that s(t):=sin(t)
t0 = 0
s0 = numpy.sin(t0)
sd0 = numpy.cos(t0)
# labdify a
fa = sympy.lambdify([t, s], a)
va = fa(t0, s0)
print (va)
#OUT: 0
# try to lambdify b
fb = sympy.lambdify([t, s, s.diff(t)], b) # this fails with syntax error
vb = fb(t0, s0, sd0)
print (vb)
Error message:
File "<string>", line 1
lambda _Dummy_142,_Dummy_143,Derivative(s(t), t): (2*_Dummy_142*_Dummy_143*Derivative(_Dummy_143, _Dummy_142) + _Dummy_143**2)
^
SyntaxError: invalid syntax
Apparently the Derivative object is not resolved correctly, how can I work around that?
As an alternative to lambdify I'm also open to using theano or cython based solutions, but I have encountered similar problems with the corresponding printers.
Any help is appreciated.
As far as I can tell, the problem originates from an incorrect/unfortunate dummification process within the lambdify function. I have written my own dummification function that I apply to the parameters as well as the expression before passing them to lambdifying.
def dummify_undefined_functions(expr):
mapping = {}
# replace all Derivative terms
for der in expr.atoms(sympy.Derivative):
f_name = der.expr.func.__name__
var_names = [var.name for var in der.variables]
name = "d%s_d%s" % (f_name, 'd'.join(var_names))
mapping[der] = sympy.Symbol(name)
# replace undefined functions
from sympy.core.function import AppliedUndef
for f in expr.atoms(AppliedUndef):
f_name = f.func.__name__
mapping[f] = sympy.Symbol(f_name)
return expr.subs(mapping)
Use like this:
params = [dummify_undefined_functions(x) for x in [t, s, s.diff(t)]]
expr = dummify_undefined_functions(b)
fb = sympy.lambdify(params, expr)
Obviously this is somewhat brittle:
no guard against name-collisions
perhaps not the best possible name-scheme: df_dxdy for Derivative(f(x,y), x, y)
it is assumed that all derivatives are of the form:
Derivative(s(t), t, ...) with s(t) being an UndefinedFunction and t a Symbol. I have no idea what will happen if any argument to Derivative is a more complex expression. I kind of think/hope that the (automatic) simplification process will reduce any more complex derivative into an expression consisting of 'basic' derivatives. But I certainly do not guard against it.
largely untested (except for my specific use-cases)
Other than that it works quite well.
First off, rather than an UndefinedFunction, you could go ahead and use the implemented_function function to tie your numerical implementation of s(t) to a symbolic function.
Then, if you are constrained to discrete numerical data defining the function whose derivative occurs in the troublesome expression, much of the time, the numerical evaluation of the derivative may come from finite differences. As an alternative, sympy can automatically replace derivative terms with finite differences, and let the resulting expression be converted to a lambda. For example:
import sympy
import numpy
from sympy.utilities.lambdify import lambdify, implemented_function
from sympy import Function
# define a parameter and an unknown function on said parameter
t = sympy.Symbol('t')
s = implemented_function(Function('s'), numpy.cos)
print('A plain ol\' expression')
a = t*s(t)**2
print(a)
print('Derivative of above:')
b = a.diff(t)
print(b)
# try to lambdify b by first replacing with finite differences
dx = 0.1
bapprox = b.replace(lambda arg: arg.is_Derivative,
lambda arg: arg.as_finite_difference(points=dx))
print('Approximation of derivatives:')
print(bapprox)
fb = sympy.lambdify([t], bapprox)
t0 = 0.0
vb = fb(t0)
print(vb)
The similar question was discussed at here
You just need to define your own function and define its derivative as another function:
def f_impl(x):
return x**2
def df_impl(x):
return 2*x
class df(sy.Function):
nargs = 1
is_real = True
_imp_ = staticmethod(df_impl)
class f(sy.Function):
nargs = 1
is_real = True
_imp_ = staticmethod(f_impl)
def fdiff(self, argindex=1):
return df(self.args[0])
t = sy.Symbol('t')
print f(t).diff().subs({t:0.1})
expr = f(t) + f(t).diff()
expr_real = sy.lambdify(t, expr)
print expr_real(0.1)

SymPy/SciPy: solving a system of ordinary differential equations with different variables

I am new to SymPy and Python in general, and I am currently working with Python 2.7 and SymPy 0.7.5 with the objective to:
a) read a system of differential equations from a text file
b) solve the system
I already read this question and this other question, and they are almost what I am looking for, but I have an additional issue: I do not know in advance the form of the system of equations, so I cannot create the corresponding function using def inside the script, as in this example. The whole thing has to be managed at run-time.
So, here are some snippets of my code. Suppose I have a text file system.txt containing the following:
dx/dt = 0.0387*x - 0.0005*x*y
dy/dt = 0.0036*x*y - 0.1898*y
What I do is:
# imports
import sympy
import scipy
import re as regex
# define all symbols I am going to use
x = sympy.Symbol('x')
y = sympy.Symbol('y')
t = sympy.Symbol('t')
# read the file
systemOfEquations = []
with open("system.txt", "r") as fp :
for line in fp :
pattern = regex.compile(r'.+?\s+=\s+(.+?)$')
expressionString = regex.search(pattern, line) # first match ends in group(1)
systemOfEquations.append( sympy.sympify( expressionString.group(1) ) )
At this point, I am stuck with the two symbolic expressions inside the systemOfEquation list. Provided that I can read the initial conditions for the ODE system from another file, in order to use scipy.integrate.odeint, I would have to convert the system into a Python-readable function, something like:
def dX_dt(X, t=0):
return array([ 0.0387*X[0] - 0.0005*X[0]*X[1] ,
-0.1898*X[1] + 0.0036*X[0]*X[1] ])
Is there a nice way to create this at run-time? For example, write the function to another file and then import the newly created file as a function? (maybe I am being stupid here, but remember that I am relatively new to Python :-D)
I've seen that with sympy.utilities.lambdify.lambdify it's possible to convert a symbolic expression into a lambda function, but I wonder if this can help me...lambdify seems to work with one expression at the time, not with systems.
Thank you in advance for any advice :-)
EDIT:
With minimal modifications, Warren's answer worked flawlessly. I have a list of all symbols inside listOfSymbols; moreover, they appear in the same order as the columns of data X that will be used by odeint. So, the function I used is
def dX_dt(X, t):
vals = dict()
for index, s in enumerate(listOfSymbols) :
if s != time :
vals[s] = X[index]
vals[time] = t
return [eq.evalf(subs=vals) for eq in systemOfEquations]
I just make an exception for the variable 'time' in my specific problem. Thanks again! :-)
If you are going to solve the system in the same script that reads the file (so systemOfEquations is available as a global variable), and if the only variables used in systemOfEquations are x, y and possibly t, you could define dX_dt in the same file like this:
def dX_dt(X, t):
vals = dict(x=X[0], y=X[1], t=t)
return [eq.evalf(subs=vals) for eq in systemOfEquations]
dX_dt can be used in odeint. In the following ipython session, I have already run the script that creates systemOfEquations and defines dX_dt:
In [31]: odeint(dX_dt, [1,2], np.linspace(0, 1, 5))
Out[31]:
array([[ 1. , 2. ],
[ 1.00947534, 1.90904183],
[ 1.01905178, 1.82223595],
[ 1.02872997, 1.73939226],
[ 1.03851059, 1.66032942]]

Categories