I find it quite convenient to use both structs and np.ndarrays using dtypes like pyopencl.cltypes.float2. This is a clear and self documenting way to pass structured data to my kernels, as opposed to just a bunch of floats.
However I find certain behavior inconvenient. For example given:
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')])
I can construct my data structure piecemeal before passing it to the kernel by doing e.g.
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')])
which is nice. However I would also like to assign an array of size 2 to a float2, yet this fails.
a[:,1] = np.array((42,-42))
ValueError: could not broadcast input array from shape (2) into shape (3)
and I have to resort to doing this
a[:,1]['x'] = 42
a[:,1]['y'] = -42
which works, but kind of defeats the purpose of the whole thing.
Am I missing some obvious syntactic difference or is this feature just not supposed to be used this way?
EDIT: In my attempt to present the most minimal example I oversimplified the problem. hpaulj's answer is correct in that if I use explicit dtype=float2 on something of shape=(2,) it will get converted to a shape=(1,) of dtype=float2. My actual question however was more general, on how to go from say an array(shape=(foo, bar, N), dtype=np.float32) into an array(shape=(foo, bar), dtype=cltp.floatN) with N=2,3,4.
Followup question that just occurred: will an actual copy necessarily occur or can this be converted 'in place'? The actual memory layout looks compatible at first glance.
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')})
Look at the intended target, a 3 element array with the compound dtype:
In [102]: a[:,1]
Out[102]:
array([(0., 0.), (0., 0.), (0., 0.)],
dtype=[(('x', 's0'), '<f4'), (('y', 's1'), '<f4')])
While your array is 2 element int. With this construct the use of tuple doesn't change anything.
In [103]: np.array((42,-42))
Out[103]: array([ 42, -42])
But if we specify the correct dtype, we make a 0d array:
In [104]: np.array((42,-42), a.dtype)
Out[104]: array((42., -42.), dtype=[(('x', 's0'), '<f4'), (('y', 's1'), '<f4')])
Now there's no problem assigning it.
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')])
Using a recent recfunctions
addition, we can make a structured array from an appropriately shape unstructured array:
In [5]: import numpy.lib.recfunctions as rf
2d to 1d structured:
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 to 2d structured:
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')])
Or making an array from a list of tuples:
In [10]: np.array([(0,1),(2,3)], dt)
Out[10]:
array([(0., 1.), (2., 3.)],
dtype=[(('x', 's0'), '<f4'), (('y', 's1'), '<f4')])
recfunctions
often makes use of field-by-field setting:
In [16]: for name in a.dtype.names:
...: a[name][:] = np.arange(15).reshape(3,5)