我想了解如何在python中实现einsum函数.我在numpy/core/src/multiarray/einsum.c.src文件中找到了源代码,但无法完全理解.我特别想了解它是如何自动创建所需的循环的?

例如:

import numpy as np
a = np.random.rand(2,3,4,5)
b = np.random.rand(5,3,2,4)

ll = np.einsum('ijkl, ljik ->', a,b) # This should loop over all the 
# four indicies i,j,k,l. How does it create loops for these indices automatically ?

# The assume that under the hood it does the following 
sum1 = 0
for i in range(2):
    for j in range(3):
        for k in range(4):
            for l in range(5):
                sum1 = sum1 + a[i,j,k,l]*b[l,j,i,k]

提前谢谢你

ps:这个问题不是关于如何使用numpy.埃因苏姆

推荐答案

我想了解它是如何自动创建所需的循环的?

好吧,它不会像你想象的那样创建循环.在这种情况下,它创建了一个在多个数组上运行的迭代器,然后在通用主循环中使用它.在更一般的情况下,有两个主要循环:一个循环迭代输出数组项,另一个循环执行缩减.

主要功能是PyArray_EinsteinSum.在您的例子中,它采用了一条未优化的路径,并基于之前创建的迭代器(即iter)以creating a basic iteration function结尾.此函数为get_sum_of_products_function.它主要分析einsum操作,以便根据查找表(如_outstride0_specialized_table)找到要调用的最佳(乘积和)函数.在您的特定情况下,称为double_sum_of_products_outstride0_two.Numpy使用模板系统,以便在生成时自动生成此函数(*.c.src文件是基于预定义基本注释转换为*.c文件的模板文件).在这种情况下,函数是从@name@_sum_of_products_outstride0_@noplabel@生成的,一旦由C预处理器计算,它就会给出类似以下函数的结果:

static void double_sum_of_products_outstride0_two(int nop, 
                                                    char **dataptr,
                                                    npy_intp const *strides, 
                                                    npy_intp count)
{
    npy_double accum = 0;
    char *data0 = dataptr[0];
    npy_intp stride0 = strides[0];
    char *data1 = dataptr[1];
    npy_intp stride1 = strides[1];

    while (count--)
    {
        accum += (*(npy_double *)data0) * (*(npy_double *)data1);
        data0 += stride0;
        data1 += stride1;
    }

    *((npy_double *)dataptr[2]) = (accum + (*((npy_double *)dataptr[2])));
}

如您所见,只有一个主循环在先前生成的迭代器上迭代.在您的例子中,stride0stride1都等于8,data0data1是原始输入数组,dataptr是原始输出数组,count最初设置为120.请注意,两个步长都等于8这一事实初看起来令人惊讶,因为einsum不会在两个数组上连续迭代.这是因为第二个数组被复制和重新排序,因为Numpy无法基于einsum参数创建统一视图.

注意,示例代码的回退用例使用并没有特别优化,它只产生一个值.例如,对于以下代码,可以从unbuffered_loop_nop2_ndim2调用优化得多的double_sum_of_products_contig_contig_outstride0_two函数:

import numpy as np

a = np.random.rand(3, 10)
b = np.random.rand(3, 10)

for i in range(1):
    ll = np.einsum('ij, ij -> i', a, b) 

在这种情况下,double_sum_of_products_contig_contig_outstride0_two对给定的输出项执行缩减,unbuffered_loop_nop2_ndim2迭代输出array.

如果在上述代码中改用表达式ij, ij -> j,则调用函数double_sum_of_products_contig_two,该函数的操作方式与double_sum_of_products_contig_contig_outstride0_two相同,只是在归约期间读取/写入整个输出行.

Python相关问答推荐

如何在BeautifulSoup中链接Find()方法并处理无?

TARete错误:类型对象任务没有属性模型'

如何比较numPy数组中的两个图像以获取它们不同的像素

带条件计算最小值

海运图:调整行和列标签

为什么符号没有按顺序添加?

如何获得每个组的时间戳差异?

把一个pandas文件夹从juyter笔记本放到堆栈溢出问题中的最快方法?

从spaCy的句子中提取日期

如何启动下载并在不击中磁盘的情况下呈现响应?

合并与拼接并举

在二维NumPy数组中,如何 Select 内部数组的第一个和第二个元素?这可以通过索引来实现吗?

如何删除重复的文字翻拍?

如何在Python 3.9.6和MacOS Sonoma 14.3.1下安装Pyregion

如何在GEKKO中使用复共轭物

为用户输入的整数查找根/幂整数对的Python练习

合并相似列表

在第一次调用时使用不同行为的re. sub的最佳方式

#将多条一维曲线计算成其二维数组(图像)表示

Python:使用asyncio.StreamReader.readline()读取长行