
本文介绍如何在 SymPy 中定义满足线性性与对称性的自定义点积函数 dot(a, b),并通过重载 Python 函数逻辑实现自动展开(如 dot(a+b, c) → dot(a,c)+dot(b,c))和规范化(如 dot(b,a) → dot(a,b)),无需依赖符号规则引擎或外部库。
本文介绍如何在 sympy 中定义满足线性性与对称性的自定义点积函数 `dot(a, b)`,并通过重载 python 函数逻辑实现自动展开(如 `dot(a+b, c) → dot(a,c)+dot(b,c)`)和规范化(如 `dot(b,a) → dot(a,b)`),无需依赖符号规则引擎或外部库。
在 SymPy 中,直接使用 Function('dot') 创建的符号函数默认不具备代数性质;若希望其具备线性性(对第一个参数可加)和对称性(dot(a,b) = dot(b,a)),不能仅靠 Eq 规则声明——因为 SymPy 的 simplify 或 expand 不会自动匹配并应用用户定义的等式规则。正确做法是将数学语义编码进 Python 函数逻辑中,使其在被调用时即返回已展开、已规范化的表达式。
以下是一个健壮、可递归处理任意加法表达式的 dot 实现:
from sympy import Function, Add, symbols, ordered
def dot(a, b):
# 步骤1:强制对称性 —— 总按 canonically ordered (a,b) 存储,保证 dot(a,b) == dot(b,a)
A, B = ordered((a, b))
if A != a: # 若输入顺序非规范,则递归调用规范形式
return dot(B, A)
# 步骤2:利用线性性展开第一个参数(左线性)
if a.is_Add and not b.is_Add:
# 将 dot(a1 + a2 + ..., b) 拆为 sum(dot(ai, b))
return Add(*[dot(ai, b) for ai in a.args])
# 步骤3:若第二个参数也为和式,利用双线性性展开(可选增强)
if b.is_Add and not a.is_Add:
return Add(*[dot(a, bi) for bi in b.args])
# 步骤4:若两者均为和式,完全双线性展开(等价于笛卡尔积求和)
if a.is_Add and b.is_Add:
return Add(*[dot(ai, bi) for ai in a.args for bi in b.args])
# 基础情形:a、b 均为原子表达式(符号、数字等)
return Function('dot')(a, b)使用示例:
x, y, z = symbols('x y z')
print(dot(x + y, z)) # → dot(x, z) + dot(y, z)
print(dot(x, y + z)) # → dot(x, y) + dot(x, z)
print(dot(x + 1, y + 2)) # → dot(1, 2) + dot(1, y) + dot(2, x) + dot(x, y)
print(dot(y, x)) # → dot(x, y) (自动对称归一化)✅ 关键优势:该实现不依赖 simplify() 或 expand() 的内部机制,而是通过函数调用即时生成标准化结果,天然支持嵌套表达式(如 dot(dot(x+y,z), w),只要 dot 返回的是 Add 或 Function 对象即可继续参与运算)。
⚠️ 注意事项:
- 此方法本质是“构造时即展开”,而非“化简时匹配规则”,因此无法逆向合并(如 dot(a,c)+dot(b,c) 不会自动合成为 dot(a+b,c));若需双向化简,需额外编写专用 collect_dot 工具函数。
- ordered() 确保对称性基于 SymPy 的标准排序(字母序+类型优先级),避免因符号命名导致不一致。
- 若需引入标量乘法分配律(如 dot(k*a, b) = k*dot(a,b)),可在函数开头添加对 Mul 类型的分支处理。
综上,SymPy 原生暂无声明式代数规则系统(类似 Mathematica 的 UpValues),但通过精心设计的 Python 函数,完全可实现符合数学直觉的符号点积运算——简洁、可控、可扩展,且无需引入替代库(如 Cadabra 或 SageMath)。

















