如何在SymPy中将非常小的数简化为0?

3

我正在使用Python 2.7.10中的sympy 0.7.6进行一些矩阵计算。例如,

M = 
[cos(q1), -6.12323399573677e-17*sin(q1),         -1.0*sin(q1), 150*sin(q1)]
[sin(q1),  6.12323399573677e-17*cos(q1),          1.0*cos(q1), 150*sin(q1)]
[      0,                          -1.0, 6.12323399573677e-17,         445]
[      0,                             0,                    0,           1]

然后我将 M 应用于 simplify,结果如下:

M = 
[cos(q1),    0,         -1.0*sin(q1), 150*sin(q1)]
[sin(q1),    0,          1.0*cos(q1), 150*sin(q1)]
[      0, -1.0, 6.12323399573677e-17,         445]
[      0,    0,                    0,           1]

很明显,-6.12323399573677e-17*sin(q1) 简化成了 0,但是 6.12323399573677e-17 没有被简化。是否可以使用 simplify 简化这个纯数字项?


你是否拥有整个scipy堆栈,还是只有sympy? - Navin
我有scipy 0.13.0b1。 - Altrouge Brunestud
1
请注意,在当前版本的SymPy中,表达式如“-6.12323399573677e-17*sin(q1)”不再简化为“0”(这正是我希望的行为方式)。 - Wrzlprmft
3个回答

4

Sympy的nsimplify函数带有rational=True参数,可以将表达式中的浮点数转换为有理数(在给定的容差范围内)。如果小于阈值,类似6.12323399573677e-17的数字将被转换为0。因此,在您的情况下:

from sympy import Symbol, Matrix, sin, cos, nsimplify

q1 = Symbol("q1")
M = Matrix([
        [cos(q1), -6.12323e-17*sin(q1),  1.0*sin(q1), 150*sin(q1)],
        [sin(q1),  6.12323e-17*cos(q1),  1.0*cos(q1), 150*sin(q1)],
        [      0,                 -1.0, 6.123233e-17,         445],
        [      0,                    0,            0,           1],
    ])

nsimplify(M,tolerance=1e-10,rational=True)
# Matrix([
# [cos(q1),  0, -sin(q1), 150*sin(q1)],
# [sin(q1),  0,  cos(q1), 150*sin(q1)],
# [      0, -1,        0,         445],
# [      0,  0,        0,           1]])

请注意,这也将-1.0转换为-1

2

如果您使用的是矩阵(sympy.matrices.dense.MutableDenseMatrix),包括具有符号元素的矩阵,则可以使用以下函数进行转换:

def round2zero(m, e):
    for i in range(m.shape[0]):
        for j in range(m.shape[1]):
            if (isinstance(m[i,j], Float) and m[i,j] < e):
                m[i,j] = 0

例如:

from sympy import *

e = .0000001 # change according to your definition of small
x, y, z = symbols('x y z')
mlist = [[0.0, 1.0*cos(z)], [x*y, 1.05000000000000], [0,     6.12323399573677e-17]]
m = Matrix(mlist)
m
Out[4]: 
Matrix([
[0.0,           1.0*cos(z)],
[x*y,                 1.05],
[  0, 6.12323399573677e-17]])

round2zero(m,e)
m
Matrix([
[  0, 1.0*cos(z)],
[x*y,       1.05],
[  0,          0]])

非常感谢您的回复。然而,在sympy矩阵中,np.abs()不能应用于符号项。它会返回TypeError: cannot determine truth value of Abs(cos(q1)) < 0.0001。我以为这个问题会很简单解决,但似乎不是这样。我必须逐个检查每个项,看它是否为数值型。 - Altrouge Brunestud

0

一些模糊测试可以帮助检测更多可以设置为零的值:

import sympy
import random


def fuzz_simplify(matrix, min=-1.0, max=1.0, iterations=1000, tolerance=0.005):
    m = sympy.Matrix(matrix)
    free_sym = range(len(J.free_symbols))
    f = sympy.lambdify(m.free_symbols,m)

    sum = f(*[0 for i in free_sym])

    for i in range(0, iterations):
        rand_params = [random.uniform(min,max) for i in free_sym]
        sum += f(*rand_params)

    for i in range(0, J.shape[0]):
        for j in range(0, J.shape[1]):
            if sum[i,j] < tolerance:
                m[i,j] *= 0

    return m

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接