I have a very complicated non-linear function f. I want to get taylor series till degree n in a form of sympy expression for the function f at value x.
f is a regular python function not a sympy expression. Output of get_polynomial should be a sympy expression.
Is there any function that will get taylor-series of a function?
from math import sin, cos, log, e
def f(x):
# a very complicated function
y = sin(x) + cos(x) + log(abs(x)+2)**2/e**2 + sin(cos(x/2)**2) + 1
return y
def get_polynomial(function, x, degree):
# .......
# using Taylor Series
# .......
return sympy_expression_for_function_at_value_x
Output:
get_polynomial(sin, 0, 3) ---> 0 + x + 0*x**2 + (1/6)*x**3
get_polynomial(lambda x: e**x, 0, 1) --> 1 + x
In a similar manner I wanna calculate get_polynomial(f, 0, 3)
The following code is close to what you're looking for. What this does it to parse the code the of the function you wish you expand into a Taylor series, convert it into a symbolic representation using Sympy and then compute the Taylor expansion.
One limitation is that you need to have an explicit function definition so you can't use lambda expressions. This can be solved with further work. Otherwise the code does what you ask for. Note that when you define a function, it has to contain a line of the form y = ... for this code to work
from inspect import *
import sympy
def f(x):
# a very complicated function
y = sin(x) + cos(x) + log(abs(x)+2)**2/e**2 + sin(cos(x/2)**2) + 1
return y
def my_sin(x):
y = sin(x)
return y
def my_exp(x):
y = e**x
return y
x = sympy.Symbol('x')
def get_polynomial(function, x0, degree):
# parse function definition code
lines_list = getsource(function).split("\n")
for line in lines_list:
if '=' in line:
func_def = line
elements = func_def.split('=')
line = ' '.join(elements[1:])
sympy_function = sympy.sympify(line)
# compute taylor expansion symbolically
i = 0
taylor_exp = sympy.Integer(0)
while i <= degree:
taylor_exp = taylor_exp + (sympy.diff(sympy_function,x,i).subs(x,x0))/(sympy.factorial(i))*(x-x0)**i
i += 1
return taylor_exp
print (get_polynomial(my_sin,0,5))
print (get_polynomial(my_exp,0,5))
print (get_polynomial(f,0,5))
Related
Is there a way to use sympy to find/replace all standalone integers (that aren't exponents) to 1.
For example, converting the following:
F(x) = (2/x^2) + (3/x^3) + 4
To:
F(x) = (1/x^2) + (1/x^3) + 1
I've searched extensively on stackoverflow for sympy expression.match/replace/find solutions, and have tried using a Wildcard symbol to find and replace all numbers in the expression but I keep running into the issue of matching and replacing the exponents (2 and 3 in this example) as well as they are also considered numbers.
Is there a simple (pythonic) way to achieve the above?
Thanks!
setdefault used with replace is a nice way to go. The single expression below has 3 steps:
mask off powers and record
change Rationals to 1 (to handle integers in numer or denom)
restore powers
>>> from sympy.abc import x
>>> from sympy import Dummy
>>> eq = (2/x**2) + (3/x**3) + 4 + 1/x/8
>>> reps = {}
>>> eq = eq.replace(lambda x: x.is_Pow, lambda x: reps.setdefault(x, Dummy())
).replace(lambda x: x.is_Rational, lambda x: 1
).xreplace({v:k for k,v in reps.items()})
1 + 1/x + 1/x**2 + 1/x**3
You can write a function that will recurse into your expression. For any expression expr, expr.args will give you the components of that expression. expr.is_constant() will tell you if it's a constant. expr.is_Pow will tell you if it's an exponential expression, so you can choose not to drill down into these expressions.
import sympy
def get_constants(expr):
c = set()
for x in expr.args:
if x.is_constant(): c |= {x}
if not x.is_Pow:
c |= get_constants(x)
return c
Now, you can get all the constants in said expression, and replace each of these constants using expr.replace().
def replace_constants(expr, repl):
for const in get_constants(expr):
expr = expr.replace(const, repl)
return expr
With your example expression, we get:
x = sympy.symbols('x')
F = 2/x**2 + 3/x**3 + 4
G = replace_constants(F, 1)
print(F) # 4 + 2/x**2 + 3/x**3
print(G) # 1 + x**(-2) + x**(-3)
In sympy (python) it seems that, by default, terms in univarate polynomials are ordered according to decreasing degrees: highest degree first, then second to highest, and so on. So, for example, a polynomial like
x + 1 + x^3 + 3x^6
will be printed out as 3x^6 + x^3 + x + 1.
I would like to reverse this order of polynomial terms in sympy to be increasing in the degrees. For the same example, the print-out should read 1 + x + x^3 + 3x^6. A solution that globally changes some parameter in program preamble is preferred but other options are also welcome.
Here is an MWE to play around with. It is different from the actual program I am working with. One part of the actual program (not the MWE) is printing out a list of recursively defined polynomials, e.g., P_n(x) = P_(n-1)(x) + a_n * x^n. It is easier for me to compare them when they are ordered by increasing degree. This is the motivation to change the order; doing it globally would probably just keep the code more readable (aesthetically pleasing). But the MWE is just for the same simple polynomial given in example above.
import sympy as sym
from sympy import *
x = sym.Symbol('x')
polynomial = x + 1 + x**3 + 3*x**6
print(polynomial)
Output of MWE:
>>> 3*x**6 + x**3 + x + 1
Desired output for MWE:
>>> 1 + x + x**3 + 3*x**6
You can get the leading term using sympy.polys.polytools.LT:
LT(3x ** 6 + x ** 3 + x + 1) == 3x**6
So at least you can churn out terms recursively and print it in your own way.
Unfortunately I’ve been trying to find some way to print the terms in some fix order for a long while and find no solution better than this
It's seems that there isn't an explicit way to do that and I found this approach to the problem:
to modify the print-representation of the object you can subclass its type and override the corresponding printing method (for LaTeX, MathML, ...) see documentation.
In this case _sympystr is used to "generates readable representations of SymPy expressions."
Here a basic implementation:
from sympy import Poly, symbols, latex
class UPoly(Poly):
"""Modified univariative polynomial"""
def _sympystr(self, printer) -> str:
"""increasing order of powers"""
if self.is_multivariate: # or: not self.is_univariate
raise Exception('Error, Polynomial is not univariative')
x = next(iter(expr.free_symbols))
poly_print = ""
for deg, coef in sorted(self.terms()):
term = coef * x**deg[0]
if coef.is_negative:
term = -term # fix sign
poly_print += " - "
else:
poly_print += " + "
poly_print += printer._print(term)
return poly_print.lstrip(" +-")
def _latex(self, printer):
return latex(self._sympystr(printer)) # keep the order
x = symbols('x')
expr = 2*x + 6 - x**5
up = UPoly(expr)
print(up)
#6 + 2*x - x**5
print(latex(up))
#6 + 2 x - x^{5}
I am working with SymPy vectors:
from sympy import *
from sympy.vector import *
N = CoordSys3D('N')
x = symbols('x')
v = x * N.i + x**2 * N.j
vf=factor(v)
vf1=vf.as_independent(Vector)[1]
type(vf1)
# sympy.core.add.Add
I need to calculate dot(vf1,vf1). But SymPy does not evaluate the dot product:
ss = dot(vf1,vf1)
ss
# 1 + 2*Dot(N.i, N.j*x) + Dot(N.j*x, N.j*x)
I suspect, this is because vf1 has been metamorphosed into another type, i.e. sympy.core.add.Add).
Is there a way to make SymPy evaluate ss? Is there a way to cast vf1 as a sympy.vector...?
EDIT
I have written a function that does the dot product. But I need to do this the SymPy way, so I don't have to re-implement my own version of every function in sympy.vector.
Yes, the as_independent does not respect the class of Add or Mul that it is dealing with and only uses Mul/Add (instead of VectorMul/VectorAdd in your case). This can be fixed with a transform:
>>> from sympy.core.rules import Transform
>>> T = Transform(lambda x: (VectorMul if x.is_Mul else VectorAdd)(*x.args),
... lambda x: x.is_Add or x.is_Mul and any(isinstance(i,BaseVector)
... for i in x.args))
>>> vf1.xreplace(T)
N.i + x*N.j
>>> dot(_,_)
x**2 + 1
In order to calculate derivatives and other expressions I used the sympy package and said that T = sy.Symbol('T') now that I have calculated the right expression:
E= -T**2*F_deriv_T(T,rho)
where
def F_deriv_rho(T,rho):
ret = 0
for n in range(5):
for m in range(4):
inner= c[n,m]*g_rho_deriv_rho_np*g_T_np
ret += inner
return ret
that looks like this:
F_deriv_rho: [0.0 7.76971e-5*T 0.0001553942*T**2*rho
T*(-5.14488e-5*log(rho) - 5.14488e-5)*log(T) + T*(1.22574e-5*log(rho)+1.22574e-5)*log(T) + T*(1.89488e-5*log(rho) + 1.89488e-5)*log(T) + T(2.29441e-5*log(rho) + 2.29441e-5)*log(T) + T*(7.49956e-5*log(rho) + 7.49956e-5)*log(T)
T**2*(-0.0001028976*rho*log(rho) - 5.14488e-5*rho)*log(T) + T**2*(2.45148e-5*rho*log(rho) + 1.22574e-5*rho)*log(T) + T**2*(3.78976e-5*rho*log(rho) + 1.89488e-5*rho)*log(T) + T**2*(4.58882e-5*rho*log(rho) + 2.29441e-5*rho)*log(T) + T**2*(0.0001499912*rho*log(rho) + 7.49956e 5*rho)*log(T)]
with python I would like to change T (and rho) as a symbol to a value. How could I do that?
So, I would like to create 10 numbers like T_def = np.arange(2000, 10000, 800)and exchange all my sy.symbol(T) by iterating through the 10 values I created in the array.
Thanks for your help
I have found the solution according to this post:
How to substitute multiple symbols in an expression in sympy?
by usings "subs":
>>> from sympy import Symbol
>>> x, y = Symbol('x y')
>>> f = x + y
>>> f.subs({x:10, y: 20})
>>> f
30
There's more for this kinda thing here: http://docs.sympy.org/latest/tutorial/basic_operations.html
EDIT: A faster way would be by using "lamdify" as suggested by #Bjoern Dahlgren
x = Symbol('x')
f = x**2-3
def return_y_intercept(f):
return [the y-intercepts]
How is it possible using something like the structure above write a function that returns the y-intercepts of it's argument?
The y-intercept just means that you substitute 0 for x, so just do f.subs(x, 0).
Try using sympy.coeff, here, so like this:
Y-intercept as Coordinates
from sympy import Symbol
x = Symbol('x')
f = x**2-3
def return_y_intercept(f):
return [0,f.coeff(x,0)] #return coordintes of y-intercept
print return_y_intercept(f)
Output:
0,-3
Y-intercept:
from sympy import Symbol
x = Symbol('x')
f = x**2-3
def return_y_intercept(f):
return [f.coeff(x,0)] #return just the y-intercept
print return_y_intercept(f)
Output:
-3
try it on the online sympy interpreter here