pyOpenCL 向量类型的 numpy 数组与浮点数的 numpy 数组之间的交互

Interaction between numpy arrays of pyOpenCL vector types and numpy arrays of floats

我发现使用结构和 np.ndarrays 使用像 pyopencl.cltypes.float2 这样的 dtypes 非常方便。这是将结构化数据传递到我的内核的一种清晰且自我记录的方式,而不是一堆浮点数。

但是我发现某些行为不方便。例如给定:

import numpy as np
import pyopencl.cltypes as cltp

a = np.zeros((3,5), dtype=cltp.float2)


>>> a
array([[(0., 0.), (0., 0.), (0., 0.), (0., 0.), (0., 0.)],
       [(0., 0.), (0., 0.), (0., 0.), (0., 0.), (0., 0.)],
       [(0., 0.), (0., 0.), (0., 0.), (0., 0.), (0., 0.)]],
       dtype=[(('x', 's0'), '<f4'), (('y', 's1'), '<f4')])

我可以在将数据结构传递给内核之前逐步构建我的数据结构,例如

a[:,1] = (42,-42)
>>> a
array([[( 0.,   0.), (42., -42.), ( 0.,   0.), ( 0.,   0.), ( 0.,   0.)],
       [( 0.,   0.), (42., -42.), ( 0.,   0.), ( 0.,   0.), ( 0.,   0.)],
       [( 0.,   0.), (42., -42.), ( 0.,   0.), ( 0.,   0.), ( 0.,   0.)]],
       dtype=[(('x', 's0'), '<f4'), (('y', 's1'), '<f4')])

这很好。但是我也想将大小为 2 的数组分配给 float2,但这失败了。

a[:,1] = np.array((42,-42))

ValueError: could not broadcast input array from shape (2) into shape (3)

我不得不这样做

a[:,1]['x'] = 42
a[:,1]['y'] = -42

这行得通,但有点违背了整个事情的目的。

我是否遗漏了一些明显的语法差异,或者这个功能不应该以这种方式使用?

编辑: 在我试图展示最简单的例子时,我把问题简单化了。 hpaulj 的回答是正确的,因为如果我在 shape=(2,) 的某些东西上使用显式 dtype=float2,它将转换为 dtype=float2 的 shape=(1,)。 然而,我的实际问题更为笼统,关于如何从 array(shape=(foo, bar, N), dtype=np.float32) 到 array(shape=(foo, bar), dtype=cltp.floatN) N=2,3,4.

刚刚出现的后续问题:是否一定会出现实际副本,还是可以转换'in place'?实际的内存布局乍一看是兼容的。

In [99]: a=np.array([[(0., 0.), (0., 0.), (0., 0.), (0., 0.), (0., 0.)],
    ...:        [(0., 0.), (0., 0.), (0., 0.), (0., 0.), (0., 0.)],
    ...:        [(0., 0.), (0., 0.), (0., 0.), (0., 0.), (0., 0.)]],
    ...:        dtype=[(('x', 's0'), '<f4'), (('y', 's1'), '<f4')])
In [100]: a.dtype
Out[100]: dtype([(('x', 's0'), '<f4'), (('y', 's1'), '<f4')])
In [101]: a.dtype.fields
Out[101]: 
mappingproxy({'s0': (dtype('float32'), 0, 'x'),
              'x': (dtype('float32'), 0, 'x'),
              's1': (dtype('float32'), 4, 'y'),
              'y': (dtype('float32'), 4, 'y')})

查看预期目标,一个具有复合数据类型的 3 元素数组:

In [102]: a[:,1]
Out[102]: 
array([(0., 0.), (0., 0.), (0., 0.)],
      dtype=[(('x', 's0'), '<f4'), (('y', 's1'), '<f4')])

虽然你的数组是 2 元素 int。使用此构造,使用元组不会改变任何内容。

In [103]: np.array((42,-42))
Out[103]: array([ 42, -42])

但是如果我们指定了正确的 dtype,我们将创建一个 0d 数组:

In [104]: np.array((42,-42), a.dtype)
Out[104]: array((42., -42.), dtype=[(('x', 's0'), '<f4'), (('y', 's1'), '<f4')])

现在赋值没问题了。

In [105]: a[:,1] = np.array((42,-42), a.dtype)

编辑

In [3]: dt = a.dtype
In [4]: dt
Out[4]: dtype([(('x', 's0'), '<f4'), (('y', 's1'), '<f4')])

使用最近的 recfunctions 添加,我们可以从适当形状的非结构化数组创建结构化数组:

In [5]: import numpy.lib.recfunctions as rf    

2d 到 1d 结构化:

In [7]: rf.unstructured_to_structured(np.arange(1,7).reshape(3,2), dtype=dt)
Out[7]: 
array([(1., 2.), (3., 4.), (5., 6.)],
      dtype=[(('x', 's0'), '<f4'), (('y', 's1'), '<f4')])

3d 到 2d 结构化:

In [8]: rf.unstructured_to_structured(np.arange(24).reshape(4,3,2), dtype=dt)
Out[8]: 
array([[( 0.,  1.), ( 2.,  3.), ( 4.,  5.)],
       [( 6.,  7.), ( 8.,  9.), (10., 11.)],
       [(12., 13.), (14., 15.), (16., 17.)],
       [(18., 19.), (20., 21.), (22., 23.)]],
      dtype=[(('x', 's0'), '<f4'), (('y', 's1'), '<f4')])

或者从元组列表创建数组:

In [10]: np.array([(0,1),(2,3)], dt)
Out[10]: 
array([(0., 1.), (2., 3.)],
      dtype=[(('x', 's0'), '<f4'), (('y', 's1'), '<f4')])

recfunctions经常使用逐字段设置:

In [16]: for name in a.dtype.names:
...:     a[name][:] = np.arange(15).reshape(3,5)