简体   繁体   English

我如何让 numpy.einsum 与 sympy 一起玩?

[英]How do I get numpy.einsum to play well with sympy?

Ok, so I have several, multi-dimensional numpy arrays of sympy objects (expressions).好的,所以我有几个 sympy 对象(表达式)的多维 numpy 数组。 For example:例如:

A = array([[1.0*cos(z0)**2 + 1.0, 1.0*cos(z0)],
          [1.0*cos(z0), 1.00000000000000]], dtype=object)

and so on.等等。

What I would like to do is multiply several of these arrays using einsum, since I already have the syntax for that from a numerical calculation I was doing earlier.我想做的是使用 einsum 将这些数组中的几个相乘,因为我已经从我之前进行的数值计算中获得了语法。 The problem is, when I try to do something like问题是,当我尝试做类似的事情时

einsum('ik,jkim,j', A, B, C)

I get a type error:我收到一个类型错误:

TypeError: invalid data type for einsum

Sure, so a quick search on Google shows me einsum probably can't do this, but no reason as to why.当然,所以在谷歌上快速搜索告诉我 einsum 可能无法做到这一点,但没有理由说明为什么。 In particular, calling the numpy.dot() and numpy.tensordot() functions on those arrays works like a charm.特别是,在这些数组上调用 numpy.dot() 和 numpy.tensordot() 函数就像一个魅力。 I could use tensordot to do what I need, but my brain hurts when I think about having to replace fifty or so Einsten summations like the one above (where the order of the indeces is very important) with nested tensordot calls.可以使用 tensordot 来做我需要的事情,但是当我想到必须用嵌套的 tensordot 调用替换五十左右的 Einsten 求和时,我的大脑会受伤(其中 indeces 的顺序非常重要)。 Even more nightmarish is the though of having to debug that code and hunt for that one misplaced index swap.更可怕的是必须调试该代码并寻找那个错位的索引交换。

Long story short, does anyone know why tensordot works with objects but einsum will not?长话短说,有谁知道为什么 tensordot 可以处理对象而 einsum 不行? Any suggestions towards a workaround?对解决方法的任何建议? If not, any suggestions as to how I would go about writing my own wrapper to nested tensordot calls that is somewhat similar to the einsum notation (numbers instead of letters are fine)?如果没有,关于如何将自己的包装器编写为嵌套的 tensordot 调用的任何建议,这有点类似于 einsum 表示法(数字而不是字母很好)?

Einsum basically supersedes tensordot (not dot, because dot is normally using optimized linear algebra packages), code wise it is completely different. Einsum 基本上取代了 tensordot(不是 dot,因为 dot 通常使用优化的线性代数包),在代码方面它是完全不同的。

Here is an object einsum, its untested (for more complex things), but I think it should work... Doing the same thing in C is probably even simpler since you can steal everything but the loop itself from the real einsum function.这是一个对象 einsum,它未经测试(对于更复杂的事情),但我认为它应该可以工作......在 C 中做同样的事情可能更简单,因为你可以从真正的 einsum 函数中窃取除循环本身之外的所有内容。 So if you feel like it, implement it and make more people happy...所以,如果你喜欢它,就实施它,让更多的人开心……

https://gist.github.com/seberg/5236560 https://gist.github.com/seberg/5236560

I will not guarantee anything, especially not for weirder corner cases.我不会保证任何事情,尤其是对于更奇怪的极端情况。 Of course you can translate einsum notation to tensordot notation too I am sure, and that is probably a bit faster since the loops would end up being mostly in C...当然,您也可以将 einsum 表示法转换为 tensordot 表示法,我敢肯定,这可能会快一点,因为循环最终将主要使用 C 语言...

Interestingly enough, adding optimize="optimal" worked for me有趣的是,添加optimize="optimal"对我有用

einsum('ik,jkim,j', A, B, C) yields error, but einsum('ik,jkim,j', A, B, C)产生错误,但

einsum('ik,jkim,j', A, B, C, optimize="optimal") works perfectly well with sympy. einsum('ik,jkim,j', A, B, C, optimize="optimal")与 sympy 完美配合。

Here is a much simpler implementation that separates the einsum in multiple tensordot s.这是一个更简单的实现,它将einsum分隔为多个tensordot s。

def einsum(string, *args):
    index_groups = map(list, string.split(','))
    assert len(index_groups) == len(args)
    tensor_indices_tuples = zip(index_groups, args)
    return reduce(einsum_for_two, tensor_indices_tuples)[1]

def einsum_for_two(tensor_indices1, tensor_indices2):
    string1, tensor1 = tensor_indices1
    string2, tensor2 = tensor_indices2
    sum_over_indices = set(string1).intersection(set(string2))
    new_string = string1 + string2
    axes = ([], [])
    for i in sum_over_indices:
        new_string.remove(i)
        new_string.remove(i)
        axes[0].append(string1.index(i))
        axes[1].append(string2.index(i))
    return new_string, np.tensordot(tensor1, tensor2, axes)

First it separates the einsum arguments in tuples of (indices, tensor).首先,它将einsum参数分隔为 (indices, tensor) 的元组。 Then it reduces of the list as follows:然后它减少列表如下:

  • Takes the first two tuples, and evaluates a simple einsum_for_two on them.取前两个元组,并对它们计算一个简单的einsum_for_two It also prints out the new indices signature.它还打印出新的索引签名。
  • The value of einsum_for_two is used with the next tuple in the list as the new arguments for einsum_for_two . einsum_for_two的值与列表中的下一个元组一起用作einsum_for_two的新参数。
  • Continues until there is only tuple left.继续直到只剩下元组。 The indices signature is discarded and only the tensor is returned.索引签名被丢弃,只返回张量。

It is probably slow (but anyway you are using object dtype ).它可能很慢(但无论如何你使用的是object dtype )。 It does not do many correctness checks on the input.它不会对输入进行很多正确性检查。

As @seberg noted, my code does not work for traces of tensors.正如@seberg 所指出的,我的代码不适用于张量的痕迹。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM