
本文介绍如何在 SymPy 中定义满足线性性(对第一变量可加)和对称性(dot(a,b) = dot(b,a))的自定义 dot 函数,并通过递归重写规则实现表达式的自动 expand 与 simplify;同时提供简洁、健壮的实现方案及替代思路。
本文介绍如何在 sympy 中定义满足线性性(对第一变量可加)和对称性(dot(a,b) = dot(b,a))的自定义 `dot` 函数,并通过递归重写规则实现表达式的自动 `expand` 与 `simplify`;同时提供简洁、健壮的实现方案及替代思路。
在符号计算中,常需定义满足特定代数性质(如双线性、对称性、正定性)的抽象运算符。SymPy 原生不支持直接注册用户自定义化简规则(如 rule1, rule2),但可通过函数重载 + 递归规范化的方式模拟这一行为。核心思想是:将 dot(a, b) 实现为一个 Python 函数,在调用时主动应用数学规则,返回已部分展开/标准化的表达式,而非保留未求值的 Function('dot') 原始对象。
以下是一个推荐实现:
from sympy import Function, Add, symbols, ordered
from sympy.abc import x, y, z
def dot(a, b):
# 步骤1:强制对称性 —— 总按 canonically ordered (字典序) 归一化参数顺序
A, B = ordered((a, b))
if A != a: # 若原顺序非标准,则交换并递归调用
return dot(B, A)
# 步骤2:处理左参数为和式的情况(线性展开)
if a.is_Add and not b.is_Add:
terms = a.as_ordered_terms() # 拆分为加法项(保持确定性顺序)
return Add(*[dot(term, b) for term in terms])
# 步骤3:若右参数为和式,利用对称性转为左参数处理
if b.is_Add and not a.is_Add:
return dot(b, a) # 自动触发步骤1 → 步骤2
# 步骤4:双参数均为和式 → 展开为所有项对的笛卡尔积
if a.is_Add and b.is_Add:
a_terms = a.as_ordered_terms()
b_terms = b.as_ordered_terms()
return Add(*[dot(ai, bj) for ai in a_terms for bj in b_terms])
# 步骤5:基础情形 —— 返回规范化的未求值函数对象
return Function('dot')(a, b)该实现确保:
- ✅ dot(a, b) 恒等于 dot(b, a)(通过对称归一化);
- ✅ dot(a + b, c) 自动展开为 dot(a, c) + dot(b, c);
- ✅ dot(a, b + c) 同样成立(由对称性推导);
- ✅ 支持嵌套表达式,如 dot(x+1, y+2) → dot(1, 2) + dot(1, y) + dot(2, x) + dot(x, y)。
使用示例:
a, b, c = symbols("a b c")
print(dot(a + b, c)) # → dot(a, c) + dot(b, c)
print(dot(a, b + c)) # → dot(a, b) + dot(a, c) (因对称性自动处理)
print(dot(x + 1, y + 2)) # → dot(1, 2) + dot(1, y) + dot(2, x) + dot(x, y)⚠️ 注意事项:
- 此方法不依赖 sympy.simplify() 或 expand() 的内置机制,而是通过函数逻辑主动构造结果,因此需在定义表达式时直接调用 dot(...);
- 若需与 simplify() 协同工作,可封装为 Dot 类并继承 Function,配合 _eval_expand_basic 等钩子方法(进阶用法);
- 替代方案:Cadabra(专为张量/场论设计,原生支持用户定义代数规则)、Mathematica(通过 UpValues 和 Simplify 选项深度集成规则)——但二者学习成本与生态适配性远高于 SymPy 的轻量定制。
综上,对于大多数符号线性代数场景,上述 dot 函数实现兼顾正确性、可读性与扩展性,是 SymPy 中构建自定义代数运算符的实用范式。

















