删除 SymPy 系列扩展中的混合变量项
Remove mixed-variable terms in SymPy series expansion
考虑SymPy符号e
和i
的两个函数:
from sympy import Symbol, expand, Order
i = Symbol('i')
e = Symbol('e')
f = (i**3 + i**2 + i + 1)
g = (e**3 + e**2 + e + 1)
z = expand(f*g)
这将产生
z = e**3*i**3 + e**3*i**2 + e**3*i + e**3 + e**2*i**3 + e**2*i**2 + e**2*i + e**2 + e*i**3 + e*i**2 + e*i + e + i**3 + i**2 + i + 1
但是,假设 e
和 i
都很小,我们可以忽略三阶或更高阶的两项。使用 Sympy 的系列工具或简单地添加一个 O-notation Order
class 可以解决这个问题:
In : z = expand(f*g + Order(i**3) + Order(e**3))
Out: 1 + i + i**2 + e + e*i + e*i**2 + e**2 + e**2*i + e**2*i**2 + O(i**3) + O(e**3)
看起来很棒。但是,我仍然有混合术语 e**2 * i**2
。这些术语中的单个变量小于所需的截止值,因此 SymPy 保留它们。然而,数学上 small²·small² = small⁴。同样,e·i² = small·small² = small³。
至少出于我的目的,我希望删除这些混合术语。添加混合 Order
不会产生预期的结果(它似乎忽略了前两个订单)。
In : expand(f*g + Order(i**3) + Order(e**3) + Order((i**2)*(e**2)))
Out: 1 + i + i**2 + i**3 + e + e*i + e*i**2 + e*i**3 + e**2 + e**2*i + e**3 + e**3*i + O(e**2*i**2, e, i)
问题:SymPy 是否有一个简单的系统可以快速删除 n 阶项,以及 (e^a)·(i^b) 的项其中 a+b > n?
混乱的解决方案:我找到了解决这个问题的方法,但它很混乱,可能不通用。
z = expand(f*g + Order((e**2)*i) + Order(e*(i**2)))
zz = expand(z.removeO() + Order(e**3) + Order(i**3))
产生
zz = 1 + i + i**2 + e + e*i + e**2 + O(i**3) + O(e**3)
这正是我想要的。因此,具体说明我的问题:有没有一种方法可以一步完成,并且可以推广到任何 n? 此外,我的解决方案丢失了指示混合项的大 O 符号丢失。这不是必需的,但会很好。
因为你有双重限制,你必须在所有 Order
对象中指定无穷小变量(e
和 i
),即使它们没有出现在第一个参数。
原因是 Order(expr)
只会自动选择那些实际出现在 expr
中的符号作为无穷小,因此,例如 O(e)
仅适用于极限 e→ 0。
现在,Order
个具有不同限制的对象不能很好地混合,例如:
O(e*i)+O(e) == O(e*i) != O(e)+O(e*i) == O(e) # True
这会导致混乱,结果取决于添加顺序,这是一个很好的指标,表明这是应该避免的事情。
这可以通过显式指定无穷小符号(作为 Order
的附加参数)来避免,例如:
O(e*i)+O(e,e,i) == O(e,e,i)+O(e*i) == O(e,e,i) # True
我还没有找到一种方法来避免手动检查 e
和 i
的所有组合,但这可以通过简单的迭代来完成:
orders = sum( Order(e**a*i**(n-a),e,i) for a in range(n+1) )
expand(f*g+orders)
# 1 + i + i**2 + e + e*i + e**2 + O(e**2*i, e, i) + O(e*i**2, e, i) + O(i**3, e, i) + O(e**3, e, i)
如果不使用 Order,您可以尝试这样简单的操作:
>>> eq = expand(f*g) # as you defined
>>> def total_degree(e):
... x = Dummy()
... free = e.free_symbols
... if not free: return S.Zero
... for f in free:
... e = e.subs(f, x)
... return degree(e)
>>> eq.replace(lambda x: total_degree(x) > 2, lambda x: S.Zero)
e**2 + e*i + e + i**2 + i + 1
考虑SymPy符号e
和i
的两个函数:
from sympy import Symbol, expand, Order
i = Symbol('i')
e = Symbol('e')
f = (i**3 + i**2 + i + 1)
g = (e**3 + e**2 + e + 1)
z = expand(f*g)
这将产生
z = e**3*i**3 + e**3*i**2 + e**3*i + e**3 + e**2*i**3 + e**2*i**2 + e**2*i + e**2 + e*i**3 + e*i**2 + e*i + e + i**3 + i**2 + i + 1
但是,假设 e
和 i
都很小,我们可以忽略三阶或更高阶的两项。使用 Sympy 的系列工具或简单地添加一个 O-notation Order
class 可以解决这个问题:
In : z = expand(f*g + Order(i**3) + Order(e**3))
Out: 1 + i + i**2 + e + e*i + e*i**2 + e**2 + e**2*i + e**2*i**2 + O(i**3) + O(e**3)
看起来很棒。但是,我仍然有混合术语 e**2 * i**2
。这些术语中的单个变量小于所需的截止值,因此 SymPy 保留它们。然而,数学上 small²·small² = small⁴。同样,e·i² = small·small² = small³。
至少出于我的目的,我希望删除这些混合术语。添加混合 Order
不会产生预期的结果(它似乎忽略了前两个订单)。
In : expand(f*g + Order(i**3) + Order(e**3) + Order((i**2)*(e**2)))
Out: 1 + i + i**2 + i**3 + e + e*i + e*i**2 + e*i**3 + e**2 + e**2*i + e**3 + e**3*i + O(e**2*i**2, e, i)
问题:SymPy 是否有一个简单的系统可以快速删除 n 阶项,以及 (e^a)·(i^b) 的项其中 a+b > n?
混乱的解决方案:我找到了解决这个问题的方法,但它很混乱,可能不通用。
z = expand(f*g + Order((e**2)*i) + Order(e*(i**2)))
zz = expand(z.removeO() + Order(e**3) + Order(i**3))
产生
zz = 1 + i + i**2 + e + e*i + e**2 + O(i**3) + O(e**3)
这正是我想要的。因此,具体说明我的问题:有没有一种方法可以一步完成,并且可以推广到任何 n? 此外,我的解决方案丢失了指示混合项的大 O 符号丢失。这不是必需的,但会很好。
因为你有双重限制,你必须在所有 Order
对象中指定无穷小变量(e
和 i
),即使它们没有出现在第一个参数。
原因是 Order(expr)
只会自动选择那些实际出现在 expr
中的符号作为无穷小,因此,例如 O(e)
仅适用于极限 e→ 0。
现在,Order
个具有不同限制的对象不能很好地混合,例如:
O(e*i)+O(e) == O(e*i) != O(e)+O(e*i) == O(e) # True
这会导致混乱,结果取决于添加顺序,这是一个很好的指标,表明这是应该避免的事情。
这可以通过显式指定无穷小符号(作为 Order
的附加参数)来避免,例如:
O(e*i)+O(e,e,i) == O(e,e,i)+O(e*i) == O(e,e,i) # True
我还没有找到一种方法来避免手动检查 e
和 i
的所有组合,但这可以通过简单的迭代来完成:
orders = sum( Order(e**a*i**(n-a),e,i) for a in range(n+1) )
expand(f*g+orders)
# 1 + i + i**2 + e + e*i + e**2 + O(e**2*i, e, i) + O(e*i**2, e, i) + O(i**3, e, i) + O(e**3, e, i)
如果不使用 Order,您可以尝试这样简单的操作:
>>> eq = expand(f*g) # as you defined
>>> def total_degree(e):
... x = Dummy()
... free = e.free_symbols
... if not free: return S.Zero
... for f in free:
... e = e.subs(f, x)
... return degree(e)
>>> eq.replace(lambda x: total_degree(x) > 2, lambda x: S.Zero)
e**2 + e*i + e + i**2 + i + 1