为何SymPy会将明显非恒定的导数判定为常量?
为何SymPy会将明显非恒定的导数判定为常量?
我来帮你拆解这个问题的核心原因,再给你调整代码的方向~
核心问题:未求值的Derivative对象的性质
SymPy里的is_constant()方法判断的是表达式本身是否是不依赖于任何符号的“常量实体”,而你代码里用的Derivative(gk1, gk2)是一个未求值的导数表达式——它只是一个“表示导数的符号对象”,并不是一个随变量变化的函数。举个例子:
- 你写
Derivative(Δ, Γ),SymPy不会把它当成“依赖于Δ或Γ的表达式”,而是把它看作一个独立的、固定的符号化描述,所以is_constant()会返回True。 - 只有当你用
diff(Δ, Γ)直接计算出结果(这里是0),或者调用Derivative(Δ, Γ).doit()求值后,SymPy才会把它当成一个数值常量来处理。
你的代码里的两个关键问题
一阶雅可比矩阵用了未求值的导数
你在partials_1st_order()里,当gk1≠gk2时添加了Derivative(gk1, gk2),但实际上这些导数的结果应该是0(比如Δ对Γ的导数就是0),而不是保留未求值的表达式。这直接导致二阶导数处理的都是未求值的导数对象,进而出现is_constant()全为True的情况。二阶导数也用了未求值的
Derivative
同样的,new_diff = Derivative(numerator, denominator)也是未求值的,没有实际计算导数结果,所以得到的矩阵里全是未展开的导数符号。
调整后的代码方案
要解决这个问题,我们需要把未求值的Derivative替换为直接计算导数的diff()函数,让SymPy直接给出导数结果,而不是保留符号化的描述:
from sympy import symbols, Matrix, zeros, diff gk_letters = ["Δ", "Γ", "v"] def greek_symbols(): return symbols(" ".join(gk_letters)) def partials_1st_order(): gks = greek_symbols() rows = [] for gk1 in gks: col = [] for gk2 in gks: # 用diff直接计算导数,而不是Derivative保留未求值状态 if gk1 == gk2: col.append(1) else: col.append(diff(gk1, gk2)) rows.append(col) return gks, Matrix(rows) def partials_2nd_order(): gks, jacobian = partials_1st_order() input_rows, input_cols = jacobian.shape # 二阶导数矩阵的维度:每个变量对应一阶矩阵的一个复制,求导后堆叠 y = zeros(len(gks) * input_rows, input_cols) output_chunk = 0 for denominator in gks: for input_row_num in range(input_rows): for input_col_num in range(input_cols): numerator = jacobian[input_row_num, input_col_num] # 用diff直接计算二阶导数 new_diff = diff(numerator, denominator) y[output_chunk * input_rows + input_row_num, input_col_num] = new_diff output_chunk += 1 return y
现在调用partials_2nd_order(),你会得到一个全0的矩阵——这是因为一阶雅可比里的元素都是0或1,它们的任何偏导数都是0,符合数学实际。
如果你需要保留未求值导数再处理
如果你的场景确实需要先保留未求值的导数,之后再判断哪些项求值后为0,可以先调用doit()方法求值,再做判断:
# 比如对一个未求值的导数对象 d = Derivative(1, Δ) print(d.doit()) # 输出0 print(d.doit() == 0) # 输出True,这样就能判断它的实际结果是否为0
内容来源于stack exchange
相关产品推荐
相关产品推荐

