如何关闭SymPy中Add构造函数的项重排序功能?
SymPy中如何禁止表达式自动重排?
问题背景
在SymPy中构造符号表达式时,希望完全匹配源材料的格式,但Add构造函数会自动重排项,示例如下:
>>> a, b = symbols("a b") >>> b+a a + b >>> srepr(b+a) Add(Symbol('a'), Symbol('b'))
虽然这种重排通常合理,但需要在修改前保持表达式完全匹配源材料。
根据Add的文档说明:
Add()会对参数求值,除非传入evaluate=False。求值逻辑包括:
- 扁平化处理...
- 移除单位元...
- 合并系数...
- 项排序
Add(y, x, 2) -> Add(2, x, y)
但传入evaluate=False似乎无效:
>>> srepr(Add(b, a, evaluate=False)) Add(Symbol('a'), Symbol('b'))
实际需求是生成如下格式的表达式:
⎛ 1 ⎞ ⎜─────────────────⎟ ⎜ 1 1 ⎟ ⎜─────── + ───────⎟ ⎜R⋅N + S 1 ⎟ ⎜ E + ───⎟ ⎝ s⋅C⎠ ───────────────────────────── ⎛ 1 ⎞ ⎜D + L⋅s + ─────────────────⎟ ⎜ 1 1 ⎟ ⎜ ─────── + ───────⎟ ⎜ R⋅N + S 1 ⎟ ⎜ E + ───⎟ ⎝ s⋅C⎠
但SymPy自动重排后变成:
1 ───────────────────────────────────────────────── ⎛ 1 1 ⎞ ⎛ 1 ⎞ ⎜─────── + ───────⎟⋅⎜D + L⋅s + ─────────────────⎟ ⎜N⋅R + S 1 ⎟ ⎜ 1 1 ⎟ ⎜ E + ───⎟ ⎜ ─────── + ───────⎟ ⎝ C⋅s⎠ ⎜ N⋅R + S 1 ⎟ ⎜ E + ───⎟ ⎝ C⋅s⎠
环境信息:
- SymPy 1.14.0
- Python 3.10.12
- Ubuntu 22.04.4 LTS
可行方案:使用UnevaluatedExpr包装表达式
要阻止SymPy自动重排或化简表达式,可以用UnevaluatedExpr包装需要保留原始顺序的部分,它会让表达式保持未求值状态,直到显式调用.doit()方法才会触发化简。
示例:保留Add的原始顺序
from sympy import symbols, Add, UnevaluatedExpr a, b = symbols("a b") expr = Add(UnevaluatedExpr(b), UnevaluatedExpr(a), evaluate=False) print(expr) # 输出 b + a print(srepr(expr)) # 输出 Add(UnevaluatedExpr(Symbol('b')), UnevaluatedExpr(Symbol('a')))
针对复杂分式的处理
对于需求中的分式结构,需要将分子、分母里需要保留顺序的加法部分逐层用UnevaluatedExpr包装:
from sympy import symbols, UnevaluatedExpr, Add R, N, S, E, s, C, D, L = symbols("R N S E s C D L") # 定义分子内的加法项(保留原始顺序) inner_add = Add(UnevaluatedExpr(1/(R*N + S)), UnevaluatedExpr(1/(E + 1/(s*C))), evaluate=False) numerator = 1 / inner_add # 定义分母内的加法项(保留原始顺序) outer_add = Add(UnevaluatedExpr(D), UnevaluatedExpr(L*s), UnevaluatedExpr(1/inner_add), evaluate=False) expr = numerator / outer_add print(expr)
执行后输出的表达式会完全匹配源材料的格式,不会自动重排乘法项或调整加法顺序。
注意事项
UnevaluatedExpr会完全阻止SymPy对包装部分的化简操作(包括合并同类项、移除单位元等),若后续需要化简,可调用expr.doit()触发求值。- 对于嵌套表达式,需要逐层包装需要保留顺序的子表达式,确保每一层的Add/Mul都不会触发自动重排。
内容的提问来源于stack exchange,提问作者jonathanjo
相关产品推荐
相关产品推荐

