~ubuntu-branches/ubuntu/wily/pyopencl/wily

« back to all changes in this revision

Viewing changes to pyopencl/array.py

  • Committer: Package Import Robot
  • Author(s): Dmitrijs Ledkovs
  • Date: 2013-10-22 18:08:41 UTC
  • mfrom: (2.1.9 sid)
  • Revision ID: package-import@ubuntu.com-20131022180841-1lcaecrns774y1ys
Tags: 2013.1+git20130916-1build1
No change rebuild for Boost 1.54 transition.

Show diffs side-by-side

added added

removed removed

Lines of Context:
78
78
            name = "%s%d" % (base_name, count)
79
79
 
80
80
            titles = field_names[:count]
81
 
            if len(titles) < count:
82
 
                titles.extend((count-len(titles))*[None])
 
81
 
 
82
            padded_count = count
 
83
            if count == 3:
 
84
                padded_count = 4
 
85
 
 
86
            names = ["s%d" % i for i in range(count)]
 
87
            while len(names) < padded_count:
 
88
                names.append("padding%d" % (len(names)-count))
 
89
 
 
90
            if len(titles) < len(names):
 
91
                titles.extend((len(names)-len(titles))*[None])
83
92
 
84
93
            dtype = np.dtype(dict(
85
 
                names=["s%d" % i for i in range(count)],
86
 
                formats=[base_type]*count,
 
94
                names=names,
 
95
                formats=[base_type]*padded_count,
87
96
                titles=titles))
88
97
 
89
98
            get_or_register_dtype(name, dtype)
148
157
 
149
158
    Assumes that the zeroth entry in *args* is an :class:`Array`.
150
159
    """
151
 
    # (Note that the 'return a function' bit is done by @decorator.)
152
160
 
153
161
    def kernel_runner(*args, **kwargs):
154
162
        repr_ary = args[0]
161
169
        else:
162
170
            wait_for = list(wait_for)
163
171
 
164
 
        knl = kernel_getter(*args)
 
172
        knl = kernel_getter(*args, **kwargs)
165
173
 
166
174
        gs, ls = repr_ary.get_sizes(queue,
167
175
                knl.get_work_group_info(
227
235
        ValueError.__init__(self, val)
228
236
 
229
237
 
 
238
class _copy_queue:
 
239
    pass
 
240
 
 
241
 
230
242
class Array(object):
231
243
    """A :class:`numpy.ndarray` work-alike that stores its data and performs
232
244
    its computations on the compute device.  *shape* and *dtype* work exactly
233
245
    as in :mod:`numpy`.  Arithmetic methods in :class:`Array` support the
234
246
    broadcasting of scalars. (e.g. `array+5`)
235
247
 
236
 
    *cqa* must be a :class:`pyopencl.CommandQueue` or a:class:`Context`.
 
248
    *cqa* must be a :class:`pyopencl.CommandQueue` or a :class:`pyopencl.Context`.
237
249
 
238
250
    If it is a queue, *cqa* specifies the queue in which the array carries out
239
251
    its computations by default. If a default queue (and thereby overloaded
241
253
    :class:`Context`.
242
254
 
243
255
    *cqa* will at some point be renamed *cq*, so it should be considered
244
 
    'positional-only'.
 
256
    'positional-only'. Arguments starting from 'order' should be considered
 
257
    keyword-only.
245
258
 
246
259
    *allocator* may be `None` or a callable that, upon being called with an
247
260
    argument of the number of bytes to be allocated, returns an
350
363
    .. automethod :: __getitem__
351
364
    .. automethod :: __setitem__
352
365
 
 
366
    .. automethod :: setitem
 
367
 
 
368
    .. automethod :: map_to_host
 
369
 
 
370
    .. rubric:: Comparisons, conditionals, any, all
 
371
 
 
372
    .. versionadded:: 2013.2
 
373
 
 
374
    Boolean arrays are stored as :class:`numpy.int8` because ``bool``
 
375
    has an unspecified size in the OpenCL spec.
 
376
 
 
377
    .. automethod :: __nonzero__
 
378
 
 
379
        Only works for device scalars. (i.e. "arrays" with ``shape == ()``.)
 
380
 
 
381
    .. automethod :: any
 
382
    .. automethod :: all
 
383
 
 
384
    .. automethod :: __eq__
 
385
    .. automethod :: __ne__
 
386
    .. automethod :: __lt__
 
387
    .. automethod :: __le__
 
388
    .. automethod :: __gt__
 
389
    .. automethod :: __ge__
353
390
    """
354
391
 
355
 
    __array_priority__ = 10
 
392
    __array_priority__ = 100
356
393
 
357
394
    def __init__(self, cqa, shape, dtype, order="C", allocator=None,
358
395
            data=None, offset=0, queue=None, strides=None, events=None):
492
529
        return _ArrayFlags(self)
493
530
 
494
531
    def _new_with_changes(self, data, offset, shape=None, dtype=None,
495
 
            strides=None, queue=None):
 
532
            strides=None, queue=_copy_queue):
496
533
        """
497
534
        :arg data: *None* means alocate a new array.
498
535
        """
502
539
            dtype = self.dtype
503
540
        if strides is None:
504
541
            strides = self.strides
505
 
        if queue is None:
 
542
        if queue is _copy_queue:
506
543
            queue = self.queue
507
544
 
508
545
        if queue is not None:
519
556
                    events=self.events)
520
557
 
521
558
    def with_queue(self, queue):
522
 
        """Return a copy of *self* with the default queue set to *queue*."""
523
 
 
524
 
        assert queue.context == self.context
 
559
        """Return a copy of *self* with the default queue set to *queue*.
 
560
 
 
561
        *None* is allowed as a value for *queue*.
 
562
 
 
563
        .. versionadded:: 2013.1
 
564
        """
 
565
 
 
566
        if queue is not None:
 
567
            assert queue.context == self.context
 
568
 
525
569
        return self._new_with_changes(self.base_data, self.offset,
526
570
                queue=queue)
527
571
 
610
654
    @elwise_kernel_runner
611
655
    def _axpbyz(out, afac, a, bfac, b, queue=None):
612
656
        """Compute ``out = selffac * self + otherfac*other``,
613
 
        where `other` is a vector.."""
 
657
        where *other* is an array."""
614
658
        assert out.shape == a.shape
 
659
        assert out.shape == b.shape
615
660
 
616
661
        return elementwise.get_axpbyz_kernel(
617
662
                out.context, a.dtype, b.dtype, out.dtype)
619
664
    @staticmethod
620
665
    @elwise_kernel_runner
621
666
    def _axpbz(out, a, x, b, queue=None):
622
 
        """Compute ``z = a * x + b``, where `b` is a scalar."""
 
667
        """Compute ``z = a * x + b``, where *b* is a scalar."""
623
668
        a = np.array(a)
624
669
        b = np.array(b)
 
670
        assert out.shape == x.shape
625
671
        return elementwise.get_axpbz_kernel(out.context,
626
672
                a.dtype, x.dtype, b.dtype, out.dtype)
627
673
 
628
674
    @staticmethod
629
675
    @elwise_kernel_runner
630
676
    def _elwise_multiply(out, a, b, queue=None):
 
677
        assert out.shape == a.shape
 
678
        assert out.shape == b.shape
631
679
        return elementwise.get_multiply_kernel(
632
680
                a.context, a.dtype, b.dtype, out.dtype)
633
681
 
635
683
    @elwise_kernel_runner
636
684
    def _rdiv_scalar(out, ary, other, queue=None):
637
685
        other = np.array(other)
 
686
        assert out.shape == ary.shape
638
687
        return elementwise.get_rdivide_elwise_kernel(
639
688
                out.context, ary.dtype, other.dtype, out.dtype)
640
689
 
977
1026
        self._copy(result, self, queue=queue)
978
1027
        return result
979
1028
 
980
 
    # {{{ rich comparisons (or rather, lack thereof)
 
1029
    # {{{ rich comparisons, any, all
 
1030
 
 
1031
    def __nonzero__(self):
 
1032
        if self.shape == ():
 
1033
            return bool(self.get())
 
1034
        else:
 
1035
            raise ValueError("The truth value of an array with "
 
1036
                    "more than one element is ambiguous. Use a.any() or a.all()")
 
1037
 
 
1038
    def any(self, queue=None, wait_for=None):
 
1039
        from pyopencl.reduction import get_any_kernel
 
1040
        krnl = get_any_kernel(self.context, self.dtype)
 
1041
        return krnl(self, queue=queue, wait_for=wait_for)
 
1042
 
 
1043
    def all(self, queue=None, wait_for=None):
 
1044
        from pyopencl.reduction import get_all_kernel
 
1045
        krnl = get_all_kernel(self.context, self.dtype)
 
1046
        return krnl(self, queue=queue, wait_for=wait_for)
 
1047
 
 
1048
    @staticmethod
 
1049
    @elwise_kernel_runner
 
1050
    def _scalar_comparison(out, a, b, queue=None, op=None):
 
1051
        return elementwise.get_array_scalar_comparison_kernel(
 
1052
                out.context, op, a.dtype)
 
1053
 
 
1054
    @staticmethod
 
1055
    @elwise_kernel_runner
 
1056
    def _array_comparison(out, a, b, queue=None, op=None):
 
1057
        if a.shape != b.shape:
 
1058
            raise ValueError("shapes of comparison arguments do not match")
 
1059
        return elementwise.get_array_comparison_kernel(
 
1060
                out.context, op, a.dtype, b.dtype)
981
1061
 
982
1062
    def __eq__(self, other):
983
 
        raise NotImplementedError
 
1063
        if isinstance(other, Array):
 
1064
            result = self._new_like_me(np.int8)
 
1065
            self._array_comparison(result, self, other, op="==")
 
1066
            return result
 
1067
        else:
 
1068
            result = self._new_like_me(np.int8)
 
1069
            self._scalar_comparison(result, self, other, op="==")
 
1070
            return result
984
1071
 
985
1072
    def __ne__(self, other):
986
 
        raise NotImplementedError
 
1073
        if isinstance(other, Array):
 
1074
            result = self._new_like_me(np.int8)
 
1075
            self._array_comparison(result, self, other, op="!=")
 
1076
            return result
 
1077
        else:
 
1078
            result = self._new_like_me(np.int8)
 
1079
            self._scalar_comparison(result, self, other, op="!=")
 
1080
            return result
987
1081
 
988
1082
    def __le__(self, other):
989
 
        raise NotImplementedError
 
1083
        if isinstance(other, Array):
 
1084
            result = self._new_like_me(np.int8)
 
1085
            self._array_comparison(result, self, other, op="<=")
 
1086
            return result
 
1087
        else:
 
1088
            result = self._new_like_me(np.int8)
 
1089
            self._scalar_comparison(result, self, other, op="<=")
 
1090
            return result
990
1091
 
991
1092
    def __ge__(self, other):
992
 
        raise NotImplementedError
 
1093
        if isinstance(other, Array):
 
1094
            result = self._new_like_me(np.int8)
 
1095
            self._array_comparison(result, self, other, op=">=")
 
1096
            return result
 
1097
        else:
 
1098
            result = self._new_like_me(np.int8)
 
1099
            self._scalar_comparison(result, self, other, op=">=")
 
1100
            return result
993
1101
 
994
1102
    def __lt__(self, other):
995
 
        raise NotImplementedError
 
1103
        if isinstance(other, Array):
 
1104
            result = self._new_like_me(np.int8)
 
1105
            self._array_comparison(result, self, other, op="<")
 
1106
            return result
 
1107
        else:
 
1108
            result = self._new_like_me(np.int8)
 
1109
            self._scalar_comparison(result, self, other, op="<")
 
1110
            return result
996
1111
 
997
1112
    def __gt__(self, other):
998
 
        raise NotImplementedError
 
1113
        if isinstance(other, Array):
 
1114
            result = self._new_like_me(np.int8)
 
1115
            self._array_comparison(result, self, other, op=">")
 
1116
            return result
 
1117
        else:
 
1118
            result = self._new_like_me(np.int8)
 
1119
            self._scalar_comparison(result, self, other, op=">")
 
1120
            return result
999
1121
 
1000
1122
    # }}}
1001
1123
 
1066
1188
        old_itemsize = self.dtype.itemsize
1067
1189
        itemsize = np.dtype(dtype).itemsize
1068
1190
 
1069
 
        if self.shape[-1] * old_itemsize % itemsize != 0:
 
1191
        from pytools import argmin2
 
1192
        min_stride_axis = argmin2(
 
1193
                (axis, abs(stride))
 
1194
                for axis, stride in enumerate(self.strides))
 
1195
 
 
1196
        if self.shape[min_stride_axis] * old_itemsize % itemsize != 0:
1070
1197
            raise ValueError("new type not compatible with array")
1071
1198
 
1072
 
        shape = self.shape[:-1] + (self.shape[-1] * old_itemsize // itemsize,)
1073
 
        strides = tuple(
1074
 
                s * itemsize // old_itemsize
1075
 
                for s in self.strides)
 
1199
        new_shape = (
 
1200
                self.shape[:min_stride_axis]
 
1201
                + (self.shape[min_stride_axis] * old_itemsize // itemsize,)
 
1202
                + self.shape[min_stride_axis+1:])
 
1203
        new_strides = (
 
1204
                self.strides[:min_stride_axis]
 
1205
                + (self.strides[min_stride_axis] * itemsize // old_itemsize,)
 
1206
                + self.strides[min_stride_axis+1:])
1076
1207
 
1077
1208
        return self._new_with_changes(
1078
1209
                self.base_data, self.offset,
1079
 
                shape=shape, dtype=dtype,
1080
 
                strides=strides)
 
1210
                shape=new_shape, dtype=dtype,
 
1211
                strides=new_strides)
1081
1212
 
1082
 
    # }}
 
1213
    # }}}
1083
1214
 
1084
1215
    def finish(self):
1085
1216
        # undoc
1086
 
        cl.wait_for_events(self.events)
1087
 
        del self.events[:]
 
1217
        if self.events:
 
1218
            cl.wait_for_events(self.events)
 
1219
            del self.events[:]
 
1220
 
 
1221
    def map_to_host(self, queue=None, flags=None, is_blocking=True, wait_for=None):
 
1222
        """If *is_blocking*, return a :class:`numpy.ndarray` corresponding to the
 
1223
        same memory as *self*.
 
1224
 
 
1225
        If *is_blocking* is not true, return a tuple ``(ary, evt)``, where
 
1226
        *ary* is the above-mentioned array.
 
1227
 
 
1228
        The host array is obtained using :func:`pyopencl.enqueue_map_buffer`.
 
1229
        See there for further details.
 
1230
 
 
1231
        :arg flags: A combination of :class:`pyopencl.map_flags`.
 
1232
            Defaults to read-write.
 
1233
 
 
1234
        .. versionadded :: 2013.2
 
1235
        """
 
1236
 
 
1237
        if flags is None:
 
1238
            flags = cl.map_flags.READ | cl.map_flags.WRITE
 
1239
 
 
1240
        ary, evt = cl.enqueue_map_buffer(
 
1241
                queue or self.queue, self.base_data, flags, self.offset,
 
1242
                self.shape, self.dtype, strides=self.strides, wait_for=wait_for,
 
1243
                is_blocking=is_blocking)
 
1244
 
 
1245
        if is_blocking:
 
1246
            return ary
 
1247
        else:
 
1248
            return ary, evt
 
1249
 
 
1250
    # {{{ getitem/setitem
1088
1251
 
1089
1252
    def __getitem__(self, index):
1090
1253
        """
1091
1254
        .. versionadded:: 2013.1
1092
1255
        """
 
1256
 
 
1257
        if isinstance(index, Array):
 
1258
            if index.dtype.kind != "i":
 
1259
                raise TypeError(
 
1260
                        "fancy indexing is only allowed with integers")
 
1261
            if len(index.shape) != 1:
 
1262
                raise NotImplementedError(
 
1263
                        "multidimensional fancy indexing is not supported")
 
1264
            if len(self.shape) != 1:
 
1265
                raise NotImplementedError(
 
1266
                        "fancy indexing into a multi-d array is not supported")
 
1267
 
 
1268
            return take(self, index)
 
1269
 
1093
1270
        if not isinstance(index, tuple):
1094
1271
            index = (index,)
1095
1272
 
1165
1342
                shape=tuple(new_shape),
1166
1343
                strides=tuple(new_strides))
1167
1344
 
1168
 
    def __setitem__(self, subscript, value):
1169
 
        """Set the slice of *self* identified *subscript* to *value*.
1170
 
 
1171
 
        *value* is allowed to be:
1172
 
 
1173
 
        * A :class:`Array` of the same :attr:`shape` and (for now) :attr:`strides`,
1174
 
          but with potentially different :attr:`dtype`.
1175
 
        * A :class:`numpy.ndarray` of the same :attr:`shape` and (for now)
1176
 
          :attr:`strides`, but with potentially different :attr:`dtype`.
1177
 
        * A scalar.
1178
 
 
1179
 
        Non-scalar broadcasting is not currently supported.
 
1345
    def setitem(self, subscript, value, queue=None):
 
1346
        """Like :meth:`__setitem__`, but with the ability to specify
 
1347
        a *queue* for execution.
1180
1348
 
1181
1349
        .. versionadded:: 2013.1
1182
1350
        """
1183
1351
 
 
1352
        if isinstance(subscript, Array):
 
1353
            if subscript.dtype.kind != "i":
 
1354
                raise TypeError(
 
1355
                        "fancy indexing is only allowed with integers")
 
1356
            if len(subscript.shape) != 1:
 
1357
                raise NotImplementedError(
 
1358
                        "multidimensional fancy indexing is not supported")
 
1359
            if len(self.shape) != 1:
 
1360
                raise NotImplementedError(
 
1361
                        "fancy indexing into a multi-d array is supported")
 
1362
 
 
1363
            multi_put([value], subscript, out=[self], queue=self.queue)
 
1364
            return
 
1365
 
 
1366
        queue = queue or self.queue or value.queue
 
1367
 
1184
1368
        subarray = self[subscript]
1185
1369
 
1186
1370
        if isinstance(value, np.ndarray):
1187
1371
            if subarray.shape == value.shape and subarray.strides == value.strides:
1188
1372
                self.events.append(
1189
 
                        cl.enqueue_copy(self.queue, subarray.base_data,
 
1373
                        cl.enqueue_copy(queue, subarray.base_data,
1190
1374
                            value, device_offset=subarray.offset))
1191
1375
                return
1192
1376
            else:
1193
 
                value = to_device(self.queue, value, self.allocator)
 
1377
                value = to_device(queue, value, self.allocator)
1194
1378
 
1195
1379
        if isinstance(value, Array):
1196
1380
            if len(subarray.shape) != len(value.shape):
1203
1387
                raise ValueError("cannot assign between arrays of "
1204
1388
                        "differing strides")
1205
1389
 
1206
 
            self._copy(subarray, value)
 
1390
            self._copy(subarray, value, queue=queue)
1207
1391
 
1208
1392
        else:
1209
1393
            # Let's assume it's a scalar
1210
 
            subarray.fill(value)
 
1394
            subarray.fill(value, queue=queue)
 
1395
 
 
1396
    def __setitem__(self, subscript, value):
 
1397
        """Set the slice of *self* identified *subscript* to *value*.
 
1398
 
 
1399
        *value* is allowed to be:
 
1400
 
 
1401
        * A :class:`Array` of the same :attr:`shape` and (for now) :attr:`strides`,
 
1402
          but with potentially different :attr:`dtype`.
 
1403
        * A :class:`numpy.ndarray` of the same :attr:`shape` and (for now)
 
1404
          :attr:`strides`, but with potentially different :attr:`dtype`.
 
1405
        * A scalar.
 
1406
 
 
1407
        Non-scalar broadcasting is not currently supported.
 
1408
 
 
1409
        .. versionadded:: 2013.1
 
1410
        """
 
1411
        self.setitem(subscript, value)
 
1412
 
 
1413
    # }}}
1211
1414
 
1212
1415
# }}}
1213
1416
 
1329
1532
    inf.wait_for = []
1330
1533
 
1331
1534
    if isinstance(args[-1], np.dtype):
1332
 
        dtype = args[-1]
 
1535
        inf.dtype = args[-1]
1333
1536
        args = args[:-1]
1334
1537
        explicit_dtype = True
1335
1538
 
1389
1592
# }}}
1390
1593
 
1391
1594
 
1392
 
# {{{ take/put
 
1595
# {{{ take/put/concatenate/diff
1393
1596
 
1394
1597
@elwise_kernel_runner
1395
1598
def _take(result, ary, indices):
1558
1761
    vec_count = len(arrays)
1559
1762
 
1560
1763
    if out is None:
1561
 
        out = [Array(context, dest_shape, a_dtype,
 
1764
        out = [Array(queue, dest_shape, a_dtype,
1562
1765
            allocator=a_allocator, queue=queue)
1563
1766
            for i in range(vec_count)]
1564
1767
    else:
1605
1808
 
1606
1809
    return out
1607
1810
 
 
1811
 
 
1812
def concatenate(arrays, axis=0, queue=None, allocator=None):
 
1813
    """
 
1814
    .. versionadded:: 2013.1
 
1815
    """
 
1816
    # {{{ find properties of result array
 
1817
 
 
1818
    shape = None
 
1819
 
 
1820
    for i_ary, ary in enumerate(arrays):
 
1821
        queue = queue or ary.queue
 
1822
        allocator = allocator or ary.allocator
 
1823
 
 
1824
        if shape is None:
 
1825
            # first array
 
1826
            shape = list(ary.shape)
 
1827
        else:
 
1828
            if len(ary.shape) != len(shape):
 
1829
                raise ValueError("%d'th array has different number of axes "
 
1830
                        "(shold have %d, has %d)"
 
1831
                        % (i_ary, len(ary.shape), len(shape)))
 
1832
 
 
1833
            ary_shape_list = list(ary.shape)
 
1834
            if (ary_shape_list[:axis] != shape[:axis]
 
1835
                    or ary_shape_list[axis+1:] != shape[axis+1:]):
 
1836
                raise ValueError("%d'th array has residual not matching "
 
1837
                        "other arrays" % i_ary)
 
1838
 
 
1839
            shape[axis] += ary.shape[axis]
 
1840
 
 
1841
    # }}}
 
1842
 
 
1843
    shape = tuple(shape)
 
1844
    dtype = np.find_common_type([ary.dtype for ary in arrays], [])
 
1845
    result = empty(queue, shape, dtype, allocator=allocator)
 
1846
 
 
1847
    full_slice = (slice(None),) * len(shape)
 
1848
 
 
1849
    base_idx = 0
 
1850
    for ary in arrays:
 
1851
        my_len = ary.shape[axis]
 
1852
        result.setitem(
 
1853
                full_slice[:axis]
 
1854
                + (slice(base_idx, base_idx+my_len),)
 
1855
                + full_slice[axis+1:],
 
1856
                ary)
 
1857
 
 
1858
        base_idx += my_len
 
1859
 
 
1860
    return result
 
1861
 
 
1862
 
 
1863
@elwise_kernel_runner
 
1864
def _diff(result, array):
 
1865
    return elementwise.get_diff_kernel(array.context, array.dtype)
 
1866
 
 
1867
 
 
1868
def diff(array, queue=None, allocator=None):
 
1869
    """
 
1870
    .. versionadded:: 2013.2
 
1871
    """
 
1872
 
 
1873
    if len(array.shape) != 1:
 
1874
        raise ValueError("multi-D arrays are not supported")
 
1875
 
 
1876
    n, = array.shape
 
1877
 
 
1878
    queue = queue or array.queue
 
1879
    allocator = allocator or array.allocator
 
1880
 
 
1881
    result = empty(queue, (n-1,), array.dtype, allocator=allocator)
 
1882
    _diff(result, array, queue=queue)
 
1883
    return result
 
1884
 
1608
1885
# }}}
1609
1886
 
1610
1887
 
1629
1906
 
1630
1907
    if out is None:
1631
1908
        out = empty_like(then_)
1632
 
    _if_positive(out, criterion, then_, else_)
 
1909
    _if_positive(out, criterion, then_, else_, queue=queue)
1633
1910
    return out
1634
1911
 
1635
1912