Sympy调用diff()符号求导出现浮点求导等报错问题咨询
我确信发布该问题后可能会被指向相关的StackOverflow同类问答,但经过多轮检索,我确实未找到对应解决方案。
我无法正常运行Sympy的diff()求导函数:要么无法实际完成表达式求导逻辑,要么抛出Cant calculate derivative wrt错误。
初始问题复现
x = np.arange(0, np.pi * 2, 0.1) slope = diff(np.cos(x), x)
(注:已正确导入相关依赖库,上述为隔离出的问题代码段)
运行上述代码时抛出Cant calculate derivative wrt 1.00000000000000错误。最初猜测是函数尝试对变量的实际数值而非符号变量本身求导,于是移除x = np.arange(0, np.pi * 2, 0.1)定义,此时提示变量未定义;给x赋值单个数值0,又抛出First variable cannot be a number: 0错误(修改赋值数值时错误提示中的数值会同步变化)。
最后尝试传入字符串'x'作为求导变量,即写为slope = diff(np.cos(x), 'x'),代码看似运行成功,但输出的slope结果为0,明显不符合数学预期。
已花费5小时检索相关资料,官方文档与入门教程均显示写法正确,但实际运行持续报错,不清楚问题出在哪里,也希望了解其他可实现符号求导的方案。
补充报错
采纳初始建议后,测试段代码可正常运行,但整合到完整项目代码中再次报错,本次错误为'ImmutableDenseNDimArray' object has no attribute 'as_coefficient'。
完整项目代码
x=np.arange(-np.pi,np.pi,0.01) n = 127 for i in np.arange(0,n): an = quad(fc,-np.pi,np.pi)[0] * (1.0/np.pi) An.append(an) for i in np.arange(0,n): bn=quad(fs,-np.pi,np.pi)[0]*(1.0/np.pi) Bn.append(bn) for i in np.arange(0,n): if i==0.0: symsum += f'An[{i}] / 2' else: symsum += f' + (An[{i}] * cos({i} * x) + Bn[{i}] * sin({i} * x))' print(symsum) sumf = lambda x: eval(symsum) sum = sumf(x) #<--error here ranger = x x = Symbol('x') dersymsum = f'{diff(eval(symsum), x)}' print(dersymsum)
经测试,通过lambda函数执行字符串拼接的表达式,原本可在np.arange生成的x取值区间上正常求值;若移除级数求和求值的代码段,求导逻辑可正常运行;若将求值步骤调整到求导操作之后、使用ranger变量存储取值区间,仍会抛出相同错误。已确认np.arange仅用于生成数值取值区间,不清楚冲突原因。
初始报错原因
SymPy符号运算的基本要求是:所有求导、化简等符号操作必须作用在SymPy定义的符号对象上,不能直接传入NumPy生成的数值数组/数值做符号求导。
- 最初定义的
x = np.arange(...)是NumPy浮点数组,不属于SymPy符号变量,SymPy会将数组内的浮点数识别为求导目标,自然抛出"无法对数值求导"类错误。 - 传入字符串
'x'求导得到0,是因为当前表达式里的x是NumPy数组,在SymPy判断逻辑里属于常数,常数求导结果必然为0。
初始问题正确写法
先定义SymPy符号变量完成符号求导,需要在数值区间计算结果时,用lambdify把符号表达式转成NumPy兼容的可调用函数即可:
import numpy as np from sympy import diff, cos, Symbol, lambdify # 符号求导阶段 x = Symbol('x') sym_expr = cos(x) sym_diff = diff(sym_expr, x) # 输出-sin(x),符合数学结果 # 数值计算阶段 x_num = np.arange(0, np.pi*2, 0.1) calc_diff = lambdify(x, sym_diff, 'numpy') slope_res = calc_diff(x_num)
二次报错原因
'ImmutableDenseNDimArray' object has no attribute 'as_coefficient'报错来自两个逻辑错误:
- 变量名冲突+执行顺序错误:拼接
symsum字符串的循环执行时,x还被赋值为NumPy数组,f-string拼接时直接把整个NumPy数组拼进了表达式,而不是符号x,eval后得到的是SymPy的高维数组对象,不是合法的标量符号表达式,调用diff时自然找不到对应方法。 - 后续用
ranger = x存储数值区间的操作无效,因为字符串拼接动作发生在x = Symbol('x')赋值之前,污染已经产生。
完整代码修复
严格区分符号变量和数值变量,不要复用x同时存储符号和数值,先完成所有符号运算再做数值计算,尽量避免用eval拼接表达式减少不可控污染:
import numpy as np from sympy import Symbol, diff, cos, sin, lambdify from scipy.integrate import quad # 第一步:数值计算傅里叶系数,和符号逻辑完全隔离 n = 127 An = [] Bn = [] # 替换为实际的fc、fs函数定义 fc = lambda x: x**2 fs = lambda x: x**2 for i in range(n): an = quad(fc, -np.pi, np.pi)[0] * (1/np.pi) An.append(an) for i in range(n): bn = quad(fs, -np.pi, np.pi)[0] * (1/np.pi) Bn.append(bn) # 第二步:定义符号变量,构建符号级数表达式 x = Symbol('x') symsum = 0 for i in range(n): if i == 0: symsum += An[i]/2 else: symsum += An[i] * cos(i*x) + Bn[i] * sin(i*x) # 第三步:符号求导 dersymsum = diff(symsum, x) print("符号求导结果:", dersymsum) # 第四步:数值区间计算,数值数组单独命名避免和符号x冲突 x_range = np.arange(-np.pi, np.pi, 0.01) sum_calc = lambdify(x, symsum, 'numpy') sum_res = sum_calc(x_range) diff_calc = lambdify(x, dersymsum, 'numpy') diff_res = diff_calc(x_range)
内容的提问来源于stack exchange,提问作者ChemistCraft300

