【问题标题】:Sympy lambdify Max operabilitySympy lambdify 最大可操作性
【发布时间】:2021-06-12 00:33:19
【问题描述】:

Sympy lambdify 可用于连接 numpy 和 sympy 之间的功能。但是,我找不到使用lambdify时存在哪些缺点。例如,我有兴趣将 Max 与 sympy 和 numpy 一起使用:

f = lambdify([x], Max(x, 1), 'numpy')

f(np.array([-1,0,1]))

抛出 ValueError:

ValueError: The argument '[-1, 0, 1]' is not comparable.

另一方面,使用 dunder 方法的粗略操作可以正常工作:

f = lambdify([x], Add(x, 1), 'numpy')

f(np.array([-1,0,1]))

输出:

array([0, 1, 2])

最终,我希望 lambdify 使用包含 sympy Symbol 对象(或 sympy.tensor.array.Array)的 numpy 数组:

a = symarray('a', (3,))
f = lambdify([x], Max(x, 1), 'numpy')

f(a)

抛出类型错误:

TypeError: cannot determine truth value of Relational

我的预期输出是:

array([Max(1, a_0), Max(1, a_1), Max(1, a_2)], dtype=object)

当然,我可以使用列表推导来实现这一点。

【问题讨论】:

  • 你为什么不使用列表理解?
  • 我认为广播/减少某些轴时会很复杂,但也许 numpy 通过使用某种迭代器来支持这一点?我想 einsum 是最糟糕的,因为我可能需要自己解析下标字符串。
  • 我没有使用过symarray,但看起来您将其视为任何其他对象 dtype 数组。这意味着一些数学直接起作用,例如a+1 - 不需要lambdify。但是将“函数”应用于a(例如Max)的每个元素需要显式迭代。查看我的编辑。
  • 据我所知np.maxnp.maximum.reduce 的简写,就像np.sumnp.add.reduce 的简写一样。我可以按照 hpaulj 的建议使用 frompyfunc 以元素方式将 np.maximumnp.add 应用于对象数组。另一方面,我发现很难应用像reduce 这样的泛化函数。

标签: numpy sympy


【解决方案1】:

help(f) 显示:

Help on function _lambdifygenerated:

_lambdifygenerated(x)
    Created with lambdify. Signature:
    
    func(x)
    
    Expression:
    
    Max(1, x)
    
    Source code:
    
    def _lambdifygenerated(x):
        return (amax((1,x), axis=0))

尝试将此应用于一维数组有几个问题。

In [62]: a
Out[62]: array([-1,  0,  1])

首先它会尝试从(1,x) 创建一个数组,导致出现不规则数组警告。

In [63]: f(a)
/usr/local/lib/python3.8/dist-packages/numpy/core/fromnumeric.py:87: VisibleDeprecationWarning: Creating an ndarray from ragged nested sequences (which is a list-or-tuple of lists-or-tuples-or ndarrays with different lengths or shapes) is deprecated. If you meant to do this, you must specify 'dtype=object' when creating the ndarray.
  return ufunc.reduce(obj, axis, dtype, out, **passkwargs)
---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
<ipython-input-63-730f2a0fa0b7> in <module>
----> 1 f(a)

<lambdifygenerated-4> in _lambdifygenerated(x)
      1 def _lambdifygenerated(x):
----> 2     return (amax((1,x), axis=0))

<__array_function__ internals> in amax(*args, **kwargs)
....
ValueError: The truth value of an array with more than one element is ambiguous. Use a.any() or a.all()

然后它通过尝试将a1 进行比较而产生歧义,其结果是一个布尔数组。

lambdify 是从sympynumpy 的相对“愚蠢”的词典翻译器,很容易失败。

Add 的代码是

def _lambdifygenerated(x):
    return (x + 1)

这是一个有效的numpy 表达式。

您的symarray 案例:

In [72]: a = symarray('a', (3,))

In [73]: a
Out[73]: array([a_0, a_1, a_2], dtype=object)

同样,np.array((1,a)) 创建了一个参差不齐的数组。 max.reduce 将数组与标量进行比较。

Add 有效,实际上是对 a 数组的元素进行列表理解:

In [74]: g=lambdify([x], Add(x, 1), 'numpy')
In [75]: g(a)
Out[75]: array([a_0 + 1, a_1 + 1, a_2 + 1], dtype=object)

但我们不需要lambdify 来执行此操作:

In [83]: a+1
Out[83]: array([a_0 + 1, a_1 + 1, a_2 + 1], dtype=object)

A*A.T 的计算方式:

In [89]: a*a[:,None]
Out[89]: 
array([[a_0**2, a_0*a_1, a_0*a_2],
       [a_0*a_1, a_1**2, a_1*a_2],
       [a_0*a_2, a_1*a_2, a_2**2]], dtype=object)

In [90]: _.sum(axis=1)
Out[90]: 
array([a_0**2 + a_0*a_1 + a_0*a_2, a_0*a_1 + a_1**2 + a_1*a_2,
       a_0*a_2 + a_1*a_2 + a_2**2], dtype=object)

您的Max 可以通过frompyfunc 应用于a

In [91]: f=np.frompyfunc(lambda x: Max(1,x),1,1)
In [93]: f(a)
Out[93]: array([Max(1, a_0), Max(1, a_1), Max(1, a_2)], dtype=object)

这类似于:

In [94]: np.array([Max(1,x) for x in a])
Out[94]: array([Max(1, a_0), Max(1, a_1), Max(1, a_2)], dtype=object)

速度大致相同,但在使用 broadcasting 等时灵活性更高。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-11-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多