Sympy中的多元泰勒近似
我想用 sympy
写一个多维的泰勒近似,这个近似要满足以下几点:
- 尽量使用内置的代码,
- 计算一个有两个变量的函数的截断泰勒近似,
- 返回的结果要 不包含 大O余项,比如
sin(x)=x - x**3/6 + O(x**4)
这样的形式。
这是我目前尝试的内容:
方法一
简单来说,可以对每个变量分别使用 series
命令两次,但不幸的是,这样做并不奏效,下面这个例子展示了对于函数 sin(x*cos(y))
的情况:
sp.sin(x*sp.cos(y)).series(x,x0=0,n=3).series(y,x0=0,n=3)
>>> NotImplementedError: not sure of order of O(y**3) + O(x**3)
方法二
根据这篇帖子,我首先写了一个一维的泰勒近似:
def taylor_approximation(expr, x, max_order):
taylor_series = expr.series(x=x, n=None)
return sum([next(taylor_series) for i in range(max_order)])
用一维的例子检查时效果很好
mport sympy as sp
x=sp.Symbol('x')
y=sp.Symbol('y')
taylor_approximation(sp.sin(x*sp.cos(y)),x,3)
>>> x**5*cos(y)**5/120 - x**3*cos(y)**3/6 + x*cos(y)
但是,如果我现在尝试对 x
和 y
进行链式调用来做两个展开,sympy 就卡住了
# this does not work
taylor_approximation(taylor_approximation(sp.sin(x*sp.cos(y)),x,3),y,3)
有没有人知道怎么解决这个问题,或者有没有其他方法可以实现?
3 个回答
1
多元泰勒展开
def mtaylor(funexpr,x,mu,order=1):
nvars = len(x)
hlist = ['__h' + str(i+1) for i in range(nvars)]
command=''
command="symbols('"+' '.join(hlist) +"')"
hvar = eval(command)
#mtaylor is utaylor for specificly defined function
t = symbols('t')
#substitution
loc_funexpr = funexpr
for i in range(nvars):
locvar = x[i]
locsubs = mu[i]+t*hvar[i]
loc_funexpr = loc_funexpr.subs(locvar,locsubs)
#calculate taylorseries
g = 0
for i in range(order+1):
g+=loc_funexpr.diff(t,i).subs(t,0)*t**i/math.factorial(i)
#resubstitute
for i in range(nvars):
g = g.subs(hlist[i],x[i]-mu[i])
g = g.subs(t,1)
return g
测试某个函数
x1,x2,x3,x4,x5 = symbols('x1 x2 x3 x4 x5')
funexpr=1+x1+x2+x1*x2+x1**3
funexpr=cos(funexpr)
x=[x1,x2,x3,x4,x5]
mu=[1,1,1,1,1]
mygee = mtaylor(funexpr,x,mu,order=4)
print(mygee)
6
这里有一个多变量的泰勒级数展开,可以用在Sympy这个工具上:
def Taylor_polynomial_sympy(function_expression, variable_list, evaluation_point, degree):
"""
Mathematical formulation reference:
https://math.libretexts.org/Bookshelves/Calculus/Supplemental_Modules_(Calculus)/Multivariable_Calculus/3%3A_Topics_in_Partial_Derivatives/Taylor__Polynomials_of_Functions_of_Two_Variables
:param function_expression: Sympy expression of the function
:param variable_list: list. All variables to be approximated (to be "Taylorized")
:param evaluation_point: list. Coordinates, where the function will be expressed
:param degree: int. Total degree of the Taylor polynomial
:return: Returns a Sympy expression of the Taylor series up to a given degree, of a given multivariate expression, approximated as a multivariate polynomial evaluated at the evaluation_point
"""
from sympy import factorial, Matrix, prod
import itertools
n_var = len(variable_list)
point_coordinates = [(i, j) for i, j in (zip(variable_list, evaluation_point))] # list of tuples with variables and their evaluation_point coordinates, to later perform substitution
deriv_orders = list(itertools.product(range(degree + 1), repeat=n_var)) # list with exponentials of the partial derivatives
deriv_orders = [deriv_orders[i] for i in range(len(deriv_orders)) if sum(deriv_orders[i]) <= degree] # Discarding some higher-order terms
n_terms = len(deriv_orders)
deriv_orders_as_input = [list(sum(list(zip(variable_list, deriv_orders[i])), ())) for i in range(n_terms)] # Individual degree of each partial derivative, of each term
polynomial = 0
for i in range(n_terms):
partial_derivatives_at_point = function_expression.diff(*deriv_orders_as_input[i]).subs(point_coordinates) # e.g. df/(dx*dy**2)
denominator = prod([factorial(j) for j in deriv_orders[i]]) # e.g. (1! * 2!)
distances_powered = prod([(Matrix(variable_list) - Matrix(evaluation_point))[j] ** deriv_orders[i][j] for j in range(n_var)]) # e.g. (x-x0)*(y-y0)**2
polynomial += partial_derivatives_at_point / denominator * distances_powered
return polynomial
接下来是一个关于两个变量问题的验证,内容参考了这个链接中的练习和答案: https://math.libretexts.org/Bookshelves/Calculus/Supplemental_Modules_(Calculus)/Multivariable_Calculus/3%3A_Topics_in_Partial_Derivatives/Taylor__Polynomials_of_Functions_of_Two_Variables
# Solving the exercises in section 13.7 of https://math.libretexts.org/Bookshelves/Calculus/Supplemental_Modules_(Calculus)/Multivariable_Calculus/3%3A_Topics_in_Partial_Derivatives/Taylor__Polynomials_of_Functions_of_Two_Variables
from sympy import symbols, sqrt, atan, ln
# Exercise 1
x = symbols('x')
y = symbols('y')
function_expression = x*sqrt(y)
variable_list = [x,y]
evaluation_point = [1,4]
degree=1
print(Taylor_polynomial_sympy(function_expression, variable_list, evaluation_point, degree))
degree=2
print(Taylor_polynomial_sympy(function_expression, variable_list, evaluation_point, degree))
# Exercise 3
x = symbols('x')
y = symbols('y')
function_expression = atan(x+2*y)
variable_list = [x,y]
evaluation_point = [1,0]
degree=1
print(Taylor_polynomial_sympy(function_expression, variable_list, evaluation_point, degree))
degree=2
print(Taylor_polynomial_sympy(function_expression, variable_list, evaluation_point, degree))
# Exercise 5
x = symbols('x')
y = symbols('y')
function_expression = x**2*y + y**2
variable_list = [x,y]
evaluation_point = [1,3]
degree=1
print(Taylor_polynomial_sympy(function_expression, variable_list, evaluation_point, degree))
degree=2
print(Taylor_polynomial_sympy(function_expression, variable_list, evaluation_point, degree))
# Exercise 7
x = symbols('x')
y = symbols('y')
function_expression = ln(x**2+y**2+1)
variable_list = [x,y]
evaluation_point = [0,0]
degree=1
print(Taylor_polynomial_sympy(function_expression, variable_list, evaluation_point, degree))
degree=2
print(Taylor_polynomial_sympy(function_expression, variable_list, evaluation_point, degree))
对结果使用 simplify()
函数可能会很有帮助。
18
你可以使用 expr.removeO()
来去掉一个表达式中的大O符号。
一句话总结: expr.series(x, 0, 3).removeO().series(y, 0, 3).removeO()