pyopencl 2025.2.5__cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of pyopencl might be problematic. Click here for more details.

Files changed (47) hide show
  1. pyopencl/.libs/libOpenCL-83a5a7fd.so.1.0.0 +0 -0
  2. pyopencl/__init__.py +1995 -0
  3. pyopencl/_cl.cpython-312-x86_64-linux-gnu.so +0 -0
  4. pyopencl/_cl.pyi +2006 -0
  5. pyopencl/_cluda.py +57 -0
  6. pyopencl/_monkeypatch.py +1069 -0
  7. pyopencl/_mymako.py +17 -0
  8. pyopencl/algorithm.py +1454 -0
  9. pyopencl/array.py +3441 -0
  10. pyopencl/bitonic_sort.py +245 -0
  11. pyopencl/bitonic_sort_templates.py +597 -0
  12. pyopencl/cache.py +535 -0
  13. pyopencl/capture_call.py +200 -0
  14. pyopencl/characterize/__init__.py +463 -0
  15. pyopencl/characterize/performance.py +240 -0
  16. pyopencl/cl/pyopencl-airy.cl +324 -0
  17. pyopencl/cl/pyopencl-bessel-j-complex.cl +238 -0
  18. pyopencl/cl/pyopencl-bessel-j.cl +1084 -0
  19. pyopencl/cl/pyopencl-bessel-y.cl +435 -0
  20. pyopencl/cl/pyopencl-complex.h +303 -0
  21. pyopencl/cl/pyopencl-eval-tbl.cl +120 -0
  22. pyopencl/cl/pyopencl-hankel-complex.cl +444 -0
  23. pyopencl/cl/pyopencl-random123/array.h +325 -0
  24. pyopencl/cl/pyopencl-random123/openclfeatures.h +93 -0
  25. pyopencl/cl/pyopencl-random123/philox.cl +486 -0
  26. pyopencl/cl/pyopencl-random123/threefry.cl +864 -0
  27. pyopencl/clmath.py +282 -0
  28. pyopencl/clrandom.py +412 -0
  29. pyopencl/cltypes.py +202 -0
  30. pyopencl/compyte/.gitignore +21 -0
  31. pyopencl/compyte/__init__.py +0 -0
  32. pyopencl/compyte/array.py +241 -0
  33. pyopencl/compyte/dtypes.py +316 -0
  34. pyopencl/compyte/pyproject.toml +52 -0
  35. pyopencl/elementwise.py +1178 -0
  36. pyopencl/invoker.py +417 -0
  37. pyopencl/ipython_ext.py +70 -0
  38. pyopencl/py.typed +0 -0
  39. pyopencl/reduction.py +815 -0
  40. pyopencl/scan.py +1916 -0
  41. pyopencl/tools.py +1565 -0
  42. pyopencl/typing.py +61 -0
  43. pyopencl/version.py +11 -0
  44. pyopencl-2025.2.5.dist-info/METADATA +109 -0
  45. pyopencl-2025.2.5.dist-info/RECORD +47 -0
  46. pyopencl-2025.2.5.dist-info/WHEEL +6 -0
  47. pyopencl-2025.2.5.dist-info/licenses/LICENSE +104 -0
@@ -0,0 +1,1069 @@
1
+ from __future__ import annotations
2
+
3
+
4
+ __copyright__ = "Copyright (C) 2025 University of Illinois Board of Trustees"
5
+
6
+ __license__ = """
7
+ Permission is hereby granted, free of charge, to any person obtaining a copy
8
+ of this software and associated documentation files (the "Software"), to deal
9
+ in the Software without restriction, including without limitation the rights
10
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11
+ copies of the Software, and to permit persons to whom the Software is
12
+ furnished to do so, subject to the following conditions:
13
+
14
+ The above copyright notice and this permission notice shall be included in
15
+ all copies or substantial portions of the Software.
16
+
17
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23
+ THE SOFTWARE.
24
+ """
25
+ import inspect as _inspect
26
+ from sys import intern
27
+ from typing import (
28
+ TYPE_CHECKING,
29
+ Any,
30
+ TextIO,
31
+ TypeVar,
32
+ cast,
33
+ )
34
+ from warnings import warn
35
+
36
+ import numpy as np
37
+
38
+ import pyopencl._cl as _cl
39
+
40
+
41
+ if TYPE_CHECKING:
42
+ from collections.abc import Callable, Collection, Sequence
43
+
44
+ from numpy.typing import NDArray
45
+
46
+ from pyopencl import SVMMap
47
+ from pyopencl.typing import HasBufferInterface, KernelArg, SVMInnerT, WaitList
48
+
49
+
50
+ CONSTANT_CLASSES = tuple(
51
+ getattr(_cl, name) for name in dir(_cl)
52
+ if _inspect.isclass(getattr(_cl, name))
53
+ and name[0].islower() and name not in ["zip", "map", "range"])
54
+
55
+
56
+ BITFIELD_CONSTANT_CLASSES = (
57
+ _cl.device_type,
58
+ _cl.device_fp_config,
59
+ _cl.device_exec_capabilities,
60
+ _cl.command_queue_properties,
61
+ _cl.mem_flags,
62
+ _cl.map_flags,
63
+ _cl.kernel_arg_type_qualifier,
64
+ _cl.device_affinity_domain,
65
+ _cl.mem_migration_flags,
66
+ _cl.device_svm_capabilities,
67
+ _cl.queue_properties,
68
+ _cl.svm_mem_flags,
69
+ _cl.device_atomic_capabilities,
70
+ _cl.device_device_enqueue_capabilities,
71
+ _cl.version_bits,
72
+ )
73
+
74
+
75
+ def generic_get_cl_version(self: _cl.Platform):
76
+ import re
77
+ version_string = self.version
78
+ match = re.match(r"^OpenCL ([0-9]+)\.([0-9]+) .*$", version_string)
79
+ if match is None:
80
+ raise RuntimeError("%s %s returned non-conformant "
81
+ "platform version string '%s'" %
82
+ (type(self).__name__, self, version_string))
83
+
84
+ return int(match.group(1)), int(match.group(2))
85
+
86
+
87
+ def platform_repr(self: _cl.Platform):
88
+ return f"<pyopencl.Platform '{self.name}' at 0x{self.int_ptr:x}>"
89
+
90
+
91
+ def device_repr(self: _cl.Device):
92
+ return "<pyopencl.Device '{}' on '{}' at 0x{:x}>".format(
93
+ self.name.strip(), self.platform.name.strip(), self.int_ptr)
94
+
95
+
96
+ def device_hashable_model_and_version_identifier(self: _cl.Device):
97
+ return ("v1", self.vendor, self.vendor_id, self.name, self.version)
98
+
99
+
100
+ def device_persistent_unique_id(self: _cl.Device):
101
+ warn("Device.persistent_unique_id is deprecated. "
102
+ "Use Device.hashable_model_and_version_identifier instead.",
103
+ DeprecationWarning, stacklevel=2)
104
+ return device_hashable_model_and_version_identifier(self)
105
+
106
+
107
+ def context_repr(self: _cl.Context):
108
+ return "<pyopencl.Context at 0x{:x} on {}>".format(self.int_ptr,
109
+ ", ".join(repr(dev) for dev in self.devices))
110
+
111
+
112
+ def context_get_cl_version(self: _cl.Context):
113
+ return self.devices[0].platform._get_cl_version()
114
+
115
+
116
+ def command_queue_enter(self: _cl.CommandQueue):
117
+ return self
118
+
119
+
120
+ def command_queue_exit(self: _cl.CommandQueue, exc_type, exc_val, exc_tb):
121
+ self.finish()
122
+ self._finalize()
123
+
124
+
125
+ def command_queue_get_cl_version(self: _cl.CommandQueue):
126
+ return self.device._get_cl_version()
127
+
128
+
129
+ def program_get_build_logs(self: _cl._Program):
130
+ build_logs = []
131
+ for dev in self.get_info(_cl.program_info.DEVICES):
132
+ try:
133
+ log = self.get_build_info(dev, _cl.program_build_info.LOG)
134
+ except Exception:
135
+ log = "<error retrieving log>"
136
+
137
+ build_logs.append((dev, log))
138
+
139
+ return build_logs
140
+
141
+
142
+ def program_build(
143
+ self: _cl._Program,
144
+ options_bytes: bytes,
145
+ devices: Sequence[_cl.Device] | None = None
146
+ ) -> _cl._Program:
147
+ err = None
148
+ try:
149
+ self._build(options=options_bytes, devices=devices)
150
+ except _cl.Error as e:
151
+ msg = str(e) + "\n\n" + (75*"="+"\n").join(
152
+ f"Build on {dev}:\n\n{log}"
153
+ for dev, log in self._get_build_logs())
154
+ code = e.code
155
+ routine = e.routine
156
+
157
+ err = _cl.RuntimeError(
158
+ _cl._ErrorRecord(
159
+ msg=msg,
160
+ code=code,
161
+ routine=routine))
162
+
163
+ if err is not None:
164
+ # Python 3.2 outputs the whole list of currently active exceptions
165
+ # This serves to remove one (redundant) level from that nesting.
166
+ raise err
167
+
168
+ message = (75*"="+"\n").join(
169
+ f"Build on {dev} succeeded, but said:\n\n{log}"
170
+ for dev, log in self._get_build_logs()
171
+ if log is not None and log.strip())
172
+
173
+ if message:
174
+ if self.kind() == _cl.program_kind.SOURCE:
175
+ build_type = "From-source build"
176
+ elif self.kind() == _cl.program_kind.BINARY:
177
+ build_type = "From-binary build"
178
+ elif self.kind() == _cl.program_kind.IL:
179
+ build_type = "From-IL build"
180
+ else:
181
+ build_type = "Build"
182
+
183
+ from pyopencl import compiler_output
184
+ compiler_output("%s succeeded, but resulted in non-empty logs:\n%s"
185
+ % (build_type, message))
186
+
187
+ return self
188
+
189
+
190
+ class ProfilingInfoGetter:
191
+ event: _cl.Event
192
+
193
+ def __init__(self, event: _cl.Event):
194
+ self.event = event
195
+
196
+ def __getattr__(self, name: str):
197
+ info_cls = _cl.profiling_info
198
+
199
+ if not name.islower():
200
+ warn(f"Using non-lower-case attributes with Event.profile "
201
+ f"is deprecated. Got: '{name}', expected: '{name.lower()}'. "
202
+ "This will stop working in 2026.",
203
+ DeprecationWarning, stacklevel=2)
204
+
205
+ try:
206
+ inf_attr = getattr(info_cls, name.upper())
207
+ except AttributeError as err:
208
+ raise AttributeError("%s has no attribute '%s'"
209
+ % (type(self), name)) from err
210
+ else:
211
+ return self.event.get_profiling_info(inf_attr)
212
+
213
+ queued: int # pyright: ignore[reportUninitializedInstanceVariable]
214
+ submit: int # pyright: ignore[reportUninitializedInstanceVariable]
215
+ start: int # pyright: ignore[reportUninitializedInstanceVariable]
216
+ end: int # pyright: ignore[reportUninitializedInstanceVariable]
217
+ complete: int # pyright: ignore[reportUninitializedInstanceVariable]
218
+
219
+
220
+ kernel_old_get_info = _cl.Kernel.get_info
221
+ kernel_old_get_work_group_info = _cl.Kernel.get_work_group_info
222
+
223
+
224
+ def kernel_set_arg_types(self: _cl.Kernel, arg_types):
225
+ arg_types = tuple(arg_types)
226
+
227
+ # {{{ arg counting bug handling
228
+
229
+ # For example:
230
+ # https://github.com/pocl/pocl/issues/197
231
+ # (but Apple CPU has a similar bug)
232
+
233
+ work_around_arg_count_bug = False
234
+ warn_about_arg_count_bug = False
235
+
236
+ from pyopencl.characterize import has_struct_arg_count_bug
237
+
238
+ count_bug_per_dev = [
239
+ has_struct_arg_count_bug(dev, self.context)
240
+ for dev in self.context.devices]
241
+
242
+ from pytools import single_valued
243
+ if any(count_bug_per_dev):
244
+ if all(count_bug_per_dev):
245
+ work_around_arg_count_bug = single_valued(count_bug_per_dev)
246
+ else:
247
+ warn_about_arg_count_bug = True
248
+
249
+ # }}}
250
+
251
+ from pyopencl.invoker import generate_enqueue_and_set_args
252
+ self._set_enqueue_and_set_args(
253
+ *generate_enqueue_and_set_args(
254
+ self.function_name,
255
+ len(arg_types), self.num_args,
256
+ arg_types,
257
+ warn_about_arg_count_bug=warn_about_arg_count_bug,
258
+ work_around_arg_count_bug=work_around_arg_count_bug,
259
+ devs=self.context.devices))
260
+
261
+
262
+ def kernel_get_work_group_info(self: _cl.Kernel, param: int, device: _cl.Device):
263
+ try:
264
+ wg_info_cache = self._wg_info_cache
265
+ except AttributeError:
266
+ wg_info_cache = self._wg_info_cache = {}
267
+
268
+ cache_key = (param, device.int_ptr)
269
+ try:
270
+ return wg_info_cache[cache_key]
271
+ except KeyError:
272
+ pass
273
+
274
+ result = kernel_old_get_work_group_info(self, param, device)
275
+ wg_info_cache[cache_key] = result
276
+ return result
277
+
278
+
279
+ def kernel_capture_call(
280
+ self: _cl.Kernel,
281
+ output_file: str | TextIO,
282
+ queue: _cl.CommandQueue,
283
+ global_size: tuple[int, ...],
284
+ local_size: tuple[int, ...] | None,
285
+ *args: KernelArg,
286
+ wait_for: WaitList = None,
287
+ g_times_l: bool = False,
288
+ allow_empty_ndrange: bool = False,
289
+ global_offset: tuple[int, ...] | None = None,
290
+ ) -> None:
291
+ from pyopencl.capture_call import capture_kernel_call
292
+ capture_kernel_call(self, output_file, queue, global_size, local_size,
293
+ *args,
294
+ wait_for=wait_for,
295
+ g_times_l=g_times_l,
296
+ allow_empty_ndrange=allow_empty_ndrange,
297
+ global_offset=global_offset)
298
+
299
+
300
+ def kernel_get_info(self: _cl.Kernel, param_name: _cl.kernel_info) -> object:
301
+ val = kernel_old_get_info(self, param_name)
302
+
303
+ if isinstance(val, _cl._Program):
304
+ from pyopencl import Program
305
+ return Program(val)
306
+ else:
307
+ return val
308
+
309
+
310
+ def image_format_repr(self: _cl.ImageFormat) -> str:
311
+ return "ImageFormat({}, {})".format(
312
+ _cl.channel_order.to_string(self.channel_order,
313
+ "<unknown channel order 0x%x>"),
314
+ _cl.channel_type.to_string(self.channel_data_type,
315
+ "<unknown channel data type 0x%x>"))
316
+
317
+
318
+ def image_format_eq(self: _cl.ImageFormat, other: object):
319
+ return (isinstance(other, _cl.ImageFormat)
320
+ and self.channel_order == other.channel_order
321
+ and self.channel_data_type == other.channel_data_type)
322
+
323
+
324
+ def image_format_ne(self: _cl.ImageFormat, other: object):
325
+ return not image_format_eq(self, other)
326
+
327
+
328
+ def image_format_hash(self: _cl.ImageFormat) -> int:
329
+ return hash((type(self), self.channel_order, self.channel_data_type))
330
+
331
+
332
+ def image_init(self: _cl.Image,
333
+ context: _cl.Context,
334
+ flags: _cl.mem_flags,
335
+ format: _cl.ImageFormat,
336
+ shape: tuple[int, ...] | None = None,
337
+ pitches: tuple[int, ...] | None = None,
338
+
339
+ hostbuf: HasBufferInterface | None = None,
340
+ is_array: bool = False,
341
+ buffer: _cl.Buffer | None = None,
342
+ *,
343
+ desc: _cl.ImageDescriptor | None = None,
344
+ _through_create_image: bool = False,
345
+ ) -> None:
346
+ if hostbuf is not None and not \
347
+ (flags & (_cl.mem_flags.USE_HOST_PTR | _cl.mem_flags.COPY_HOST_PTR)):
348
+ warn("'hostbuf' was passed, but no memory flags to make use of it.",
349
+ stacklevel=2)
350
+
351
+ if desc is not None:
352
+ if shape is not None:
353
+ raise TypeError("shape may not be passed when using descriptor")
354
+ if pitches is not None:
355
+ raise TypeError("pitches may not be passed when using descriptor")
356
+ if is_array:
357
+ raise TypeError("is_array may not be passed when using descriptor")
358
+ if buffer is not None:
359
+ raise TypeError("is_array may not be passed when using descriptor")
360
+
361
+ _cl.Image._custom_init(self, context, flags, format, desc, hostbuf)
362
+
363
+ return
364
+
365
+ if shape is None and hostbuf is None:
366
+ raise _cl.Error("'shape' must be passed if 'hostbuf' is not given")
367
+
368
+ if shape is None and hostbuf is not None:
369
+ shape = hostbuf.shape
370
+
371
+ if hostbuf is None and pitches is not None:
372
+ raise _cl.Error("'pitches' may only be given if 'hostbuf' is given")
373
+
374
+ if context._get_cl_version() >= (1, 2) and _cl.get_cl_header_version() >= (1, 2):
375
+ if not _through_create_image:
376
+ warn("Non-descriptor Image constructor called. "
377
+ "This will stop working in 2026. "
378
+ "Use create_image instead (with the same arguments).",
379
+ DeprecationWarning, stacklevel=2)
380
+
381
+ if buffer is not None and is_array:
382
+ raise ValueError(
383
+ "'buffer' and 'is_array' are mutually exclusive")
384
+
385
+ if len(shape) == 3:
386
+ if buffer is not None:
387
+ raise TypeError(
388
+ "'buffer' argument is not supported for 3D arrays")
389
+ elif is_array:
390
+ image_type = _cl.mem_object_type.IMAGE2D_ARRAY
391
+ else:
392
+ image_type = _cl.mem_object_type.IMAGE3D
393
+
394
+ elif len(shape) == 2:
395
+ if buffer is not None:
396
+ raise TypeError(
397
+ "'buffer' argument is not supported for 2D arrays")
398
+ elif is_array:
399
+ image_type = _cl.mem_object_type.IMAGE1D_ARRAY
400
+ else:
401
+ image_type = _cl.mem_object_type.IMAGE2D
402
+
403
+ elif len(shape) == 1:
404
+ if buffer is not None:
405
+ image_type = _cl.mem_object_type.IMAGE1D_BUFFER
406
+ elif is_array:
407
+ raise TypeError("array of zero-dimensional images not supported")
408
+ else:
409
+ image_type = _cl.mem_object_type.IMAGE1D
410
+
411
+ else:
412
+ raise ValueError("images cannot have more than three dimensions")
413
+
414
+ desc = _cl.ImageDescriptor() \
415
+ # pylint: disable=possibly-used-before-assignment
416
+
417
+ desc.image_type = image_type
418
+ desc.shape = shape # also sets desc.array_size
419
+
420
+ if pitches is None:
421
+ desc.pitches = (0, 0)
422
+ else:
423
+ desc.pitches = pitches
424
+
425
+ desc.num_mip_levels = 0 # per CL 1.2 spec
426
+ desc.num_samples = 0 # per CL 1.2 spec
427
+ desc.buffer = buffer
428
+
429
+ _cl.Image._custom_init(self, context, flags, format, desc, hostbuf)
430
+ else:
431
+ # legacy init for CL 1.1 and older
432
+ if is_array:
433
+ raise TypeError("'is_array=True' is not supported for CL < 1.2")
434
+ # if num_mip_levels is not None:
435
+ # raise TypeError(
436
+ # "'num_mip_levels' argument is not supported for CL < 1.2")
437
+ # if num_samples is not None:
438
+ # raise TypeError(
439
+ # "'num_samples' argument is not supported for CL < 1.2")
440
+ if buffer is not None:
441
+ raise TypeError("'buffer' argument is not supported for CL < 1.2")
442
+
443
+ _cl.Image._custom_init(self, context, flags, format, shape,
444
+ pitches, hostbuf)
445
+
446
+
447
+ def image_shape(self: _cl.Image) -> tuple[int, int] | tuple[int, int, int]:
448
+ if self.type == _cl.mem_object_type.IMAGE2D:
449
+ return (self.width, self.height)
450
+ elif self.type == _cl.mem_object_type.IMAGE3D:
451
+ return (self.width, self.height, self.depth)
452
+ else:
453
+ raise _cl.LogicError("only images have shapes")
454
+
455
+
456
+ def error_str(self: _cl.Error) -> str:
457
+ val = self.what
458
+ try:
459
+ val.routine # noqa: B018
460
+ except AttributeError:
461
+ return str(val)
462
+ else:
463
+ result = ""
464
+ if val.code() != _cl.status_code.SUCCESS:
465
+ result = _cl.status_code.to_string(
466
+ val.code(), "<unknown error %d>")
467
+ routine = val.routine()
468
+ if routine:
469
+ result = f"{routine} failed: {result}"
470
+ what = val.what()
471
+ if what:
472
+ if result:
473
+ result += " - "
474
+ result += what
475
+ return result
476
+
477
+
478
+ def error_code(self: _cl.Error) -> int:
479
+ return cast("_cl._ErrorRecord", self.args[0]).code()
480
+
481
+
482
+ def error_routine(self: _cl.Error) -> str:
483
+ return cast("_cl._ErrorRecord", self.args[0]).routine()
484
+
485
+
486
+ def error_what(self: _cl.Error) -> _cl._ErrorRecord:
487
+ return cast("_cl._ErrorRecord", self.args[0])
488
+
489
+
490
+ def memory_map_enter(self: _cl.MemoryMap):
491
+ return self
492
+
493
+
494
+ def memory_map_exit(self: _cl.MemoryMap, exc_type, exc_val, exc_tb):
495
+ self.release()
496
+
497
+
498
+ def svmptr_map(
499
+ self: _cl.SVMPointer,
500
+ queue: _cl.CommandQueue,
501
+ *,
502
+ flags: int,
503
+ is_blocking: bool = True,
504
+ wait_for: WaitList = None,
505
+ size: int | None = None
506
+ ) -> SVMMap[NDArray[Any]]:
507
+ """
508
+ :arg is_blocking: If *False*, subsequent code must wait on
509
+ :attr:`SVMMap.event` in the returned object before accessing the
510
+ mapped memory.
511
+ :arg flags: a combination of :class:`pyopencl.map_flags`.
512
+ :arg size: The size of the map in bytes. If not provided, defaults to
513
+ :attr:`size`.
514
+
515
+ |std-enqueue-blurb|
516
+ """
517
+ from pyopencl import SVMMap
518
+ return SVMMap(self,
519
+ np.asarray(self.buf),
520
+ queue,
521
+ _cl._enqueue_svm_map(queue, is_blocking, flags, self, wait_for,
522
+ size=size))
523
+
524
+
525
+ def svmptr_map_ro(
526
+ self: _cl.SVMPointer,
527
+ queue: _cl.CommandQueue,
528
+ *,
529
+ is_blocking: bool = True,
530
+ wait_for: WaitList = None,
531
+ size: int | None = None
532
+ ) -> SVMMap[NDArray[Any]]:
533
+ """Like :meth:`map`, but with *flags* set for a read-only map.
534
+ """
535
+
536
+ return self.map(queue, flags=_cl.map_flags.READ,
537
+ is_blocking=is_blocking, wait_for=wait_for, size=size)
538
+
539
+
540
+ def svmptr_map_rw(
541
+ self: _cl.SVMPointer,
542
+ queue: _cl.CommandQueue,
543
+ *,
544
+ is_blocking: bool = True,
545
+ wait_for: WaitList = None,
546
+ size: int | None = None
547
+ ) -> SVMMap[NDArray[Any]]:
548
+ """Like :meth:`map`, but with *flags* set for a read-only map.
549
+ """
550
+
551
+ return self.map(queue, flags=_cl.map_flags.READ | _cl.map_flags.WRITE,
552
+ is_blocking=is_blocking, wait_for=wait_for, size=size)
553
+
554
+
555
+ def svmptr__enqueue_unmap(
556
+ self: _cl.SVMPointer,
557
+ queue: _cl.CommandQueue,
558
+ wait_for: WaitList = None
559
+ ) -> _cl.Event:
560
+ return _cl._enqueue_svm_unmap(queue, self, wait_for)
561
+
562
+
563
+ def svmptr_as_buffer(
564
+ self: _cl.SVMPointer,
565
+ ctx: _cl.Context,
566
+ *,
567
+ flags: int | None = None,
568
+ size: int | None = None
569
+ ) -> _cl.Buffer:
570
+ """
571
+ :arg ctx: a :class:`Context`
572
+ :arg flags: a combination of :class:`pyopencl.map_flags`, defaults to
573
+ read-write.
574
+ :arg size: The size of the map in bytes. If not provided, defaults to
575
+ :attr:`size`.
576
+ :returns: a :class:`Buffer` corresponding to *self*.
577
+
578
+ The memory referred to by this object must not be freed before
579
+ the returned :class:`Buffer` is released.
580
+ """
581
+
582
+ if flags is None:
583
+ flags = _cl.mem_flags.READ_WRITE | _cl.mem_flags.USE_HOST_PTR
584
+
585
+ if size is None:
586
+ size = self.size
587
+
588
+ assert self.buf is not None
589
+ return _cl.Buffer(ctx, flags, size=size, hostbuf=self.buf)
590
+
591
+
592
+ def svm_map(
593
+ self: _cl.SVM[SVMInnerT],
594
+ queue: _cl.CommandQueue,
595
+ flags: int,
596
+ is_blocking: bool = True,
597
+ wait_for: WaitList = None
598
+ ) -> SVMMap[SVMInnerT]:
599
+
600
+ """
601
+ :arg is_blocking: If *False*, subsequent code must wait on
602
+ :attr:`SVMMap.event` in the returned object before accessing the
603
+ mapped memory.
604
+ :arg flags: a combination of :class:`pyopencl.map_flags`.
605
+ :returns: an :class:`SVMMap` instance
606
+
607
+ This differs from the inherited :class:`SVMPointer.map` in that no size
608
+ can be specified, and that :attr:`mem` is the exact array produced
609
+ when the :class:`SVMMap` is used as a context manager.
610
+
611
+ |std-enqueue-blurb|
612
+ """
613
+ from pyopencl import SVMMap
614
+ return SVMMap(
615
+ self,
616
+ self.mem,
617
+ queue,
618
+ _cl._enqueue_svm_map(queue, is_blocking, flags, self, wait_for))
619
+
620
+
621
+ def svm_map_ro(
622
+ self: _cl.SVM[SVMInnerT],
623
+ queue: _cl.CommandQueue,
624
+ is_blocking: bool = True,
625
+ wait_for: WaitList = None,
626
+ ) -> SVMMap[SVMInnerT]:
627
+ """Like :meth:`map`, but with *flags* set for a read-only map."""
628
+
629
+ return self.map(queue, _cl.map_flags.READ,
630
+ is_blocking=is_blocking, wait_for=wait_for)
631
+
632
+
633
+ def svm_map_rw(
634
+ self: _cl.SVM[SVMInnerT],
635
+ queue: _cl.CommandQueue,
636
+ is_blocking: bool = True,
637
+ wait_for: WaitList = None,
638
+ ) -> SVMMap[SVMInnerT]:
639
+ """Like :meth:`map`, but with *flags* set for a read-only map."""
640
+
641
+ return self.map(queue, _cl.map_flags.READ | _cl.map_flags.WRITE,
642
+ is_blocking=is_blocking, wait_for=wait_for)
643
+
644
+
645
+ def svm__enqueue_unmap(
646
+ self: _cl.SVM[SVMInnerT],
647
+ queue: _cl.CommandQueue
648
+ ,
649
+ wait_for: WaitList = None
650
+ ) -> _cl.Event:
651
+ return _cl._enqueue_svm_unmap(queue, self, wait_for)
652
+
653
+
654
+ def to_string(
655
+ cls: type,
656
+ value: int,
657
+ default_format: str | None = None
658
+ ) -> str:
659
+ if cls._is_bitfield:
660
+ names: list[str] = []
661
+ for name in dir(cls):
662
+ attr = cast("int", getattr(cls, name))
663
+ if not isinstance(attr, int):
664
+ continue
665
+ if attr == value or attr & value:
666
+ names.append(name)
667
+ if names:
668
+ return " | ".join(names)
669
+ else:
670
+ for name in dir(cls):
671
+ if (not name.startswith("_")
672
+ and getattr(cls, name) == value):
673
+ return name
674
+
675
+ if default_format is None:
676
+ raise ValueError("a name for value %d was not found in %s"
677
+ % (value, cls.__name__))
678
+ else:
679
+ return default_format % value
680
+
681
+
682
+ def _add_functionality():
683
+ # {{{ Platform
684
+
685
+ _cl.Platform.__repr__ = platform_repr
686
+ _cl.Platform._get_cl_version = generic_get_cl_version
687
+
688
+ # }}}
689
+
690
+ # {{{ Device
691
+
692
+ _cl.Device.__repr__ = device_repr
693
+
694
+ # undocumented for now:
695
+ _cl.Device._get_cl_version = generic_get_cl_version
696
+ _cl.Device.hashable_model_and_version_identifier = property(
697
+ device_hashable_model_and_version_identifier)
698
+ _cl.Device.persistent_unique_id = property(device_persistent_unique_id)
699
+
700
+ # }}}
701
+
702
+ # {{{ Context
703
+
704
+ _cl.Context.__repr__ = context_repr
705
+ from pytools import memoize_method
706
+ _cl.Context._get_cl_version = memoize_method(context_get_cl_version)
707
+
708
+ # }}}
709
+
710
+ # {{{ CommandQueue
711
+
712
+ _cl.CommandQueue.__enter__ = command_queue_enter
713
+ _cl.CommandQueue.__exit__ = command_queue_exit
714
+ _cl.CommandQueue._get_cl_version = memoize_method(command_queue_get_cl_version)
715
+
716
+ # }}}
717
+
718
+ # {{{ _Program (the internal, non-caching version)
719
+
720
+ _cl._Program._get_build_logs = program_get_build_logs
721
+ _cl._Program.build = program_build
722
+
723
+ # }}}
724
+
725
+ # {{{ Event
726
+
727
+ _cl.Event.profile = property(ProfilingInfoGetter)
728
+
729
+ # }}}
730
+
731
+ # {{{ Kernel
732
+
733
+ _cl.Kernel.get_work_group_info = kernel_get_work_group_info
734
+
735
+ # FIXME: Possibly deprecate this version
736
+ _cl.Kernel.set_scalar_arg_dtypes = kernel_set_arg_types
737
+ _cl.Kernel.set_arg_types = kernel_set_arg_types
738
+
739
+ _cl.Kernel.capture_call = kernel_capture_call
740
+ _cl.Kernel.get_info = kernel_get_info
741
+
742
+ # }}}
743
+
744
+ # {{{ ImageFormat
745
+
746
+ _cl.ImageFormat.__repr__ = image_format_repr
747
+ _cl.ImageFormat.__eq__ = image_format_eq
748
+ _cl.ImageFormat.__ne__ = image_format_ne
749
+ _cl.ImageFormat.__hash__ = image_format_hash
750
+
751
+ # }}}
752
+
753
+ # {{{ Image
754
+
755
+ _cl.Image.__init__ = image_init
756
+ _cl.Image.shape = property(image_shape)
757
+
758
+ # }}}
759
+
760
+ # {{{ Error
761
+
762
+ _cl.Error.__str__ = error_str
763
+ _cl.Error.code = property(error_code)
764
+ _cl.Error.routine = property(error_routine)
765
+ _cl.Error.what = property(error_what)
766
+
767
+ # }}}
768
+
769
+ # {{{ MemoryMap
770
+
771
+ _cl.MemoryMap.__doc__ = """
772
+ This class may also be used as a context manager in a ``with`` statement.
773
+ The memory corresponding to this object will be unmapped when
774
+ this object is deleted or :meth:`release` is called.
775
+
776
+ .. automethod:: release
777
+ """
778
+ _cl.MemoryMap.__enter__ = memory_map_enter
779
+ _cl.MemoryMap.__exit__ = memory_map_exit
780
+
781
+ # }}}
782
+
783
+ # {{{ SVMPointer
784
+
785
+ if _cl.get_cl_header_version() >= (2, 0):
786
+ _cl.SVMPointer.__doc__ = """A base class for things that can be passed to
787
+ functions that allow an SVM pointer, e.g. kernel enqueues and memory
788
+ copies.
789
+
790
+ Objects of this type cannot currently be directly created or
791
+ implemented in Python. To obtain objects implementing this type,
792
+ consider its subtypes :class:`SVMAllocation` and :class:`SVM`.
793
+
794
+
795
+ .. property:: svm_ptr
796
+
797
+ Gives the SVM pointer as an :class:`int`.
798
+
799
+ .. property:: size
800
+
801
+ An :class:`int` denoting the size in bytes, or *None*, if the size
802
+ of the SVM pointed to is not known.
803
+
804
+ *Most* objects of this type (e.g. instances of
805
+ :class:`SVMAllocation` and :class:`SVM` know their size, so that,
806
+ for example :class:`enqueue_copy` will automatically copy an entire
807
+ :class:`SVMAllocation` when a size is not explicitly specified.
808
+
809
+ .. automethod:: map
810
+ .. automethod:: map_ro
811
+ .. automethod:: map_rw
812
+ .. automethod:: as_buffer
813
+ .. property:: buf
814
+
815
+ An opaque object implementing the :c:func:`Python buffer protocol
816
+ <PyObject_GetBuffer>`. It exposes the pointed-to memory as
817
+ a one-dimensional buffer of bytes, with the size matching
818
+ :attr:`size`.
819
+
820
+ No guarantee is provided that two references to this attribute
821
+ result in the same object.
822
+ """
823
+
824
+ if _cl.get_cl_header_version() >= (2, 0):
825
+ _cl.SVMPointer.map = svmptr_map
826
+ _cl.SVMPointer.map_ro = svmptr_map_ro
827
+ _cl.SVMPointer.map_rw = svmptr_map_rw
828
+ _cl.SVMPointer._enqueue_unmap = svmptr__enqueue_unmap
829
+ _cl.SVMPointer.as_buffer = svmptr_as_buffer
830
+
831
+ # }}}
832
+
833
+ # {{{ SVMAllocation
834
+
835
+ if _cl.get_cl_header_version() >= (2, 0):
836
+ _cl.SVMAllocation.__doc__ = """
837
+ Is a :class:`SVMPointer`.
838
+
839
+ .. versionadded:: 2016.2
840
+
841
+ .. automethod:: __init__
842
+
843
+ :arg flags: See :class:`svm_mem_flags`.
844
+ :arg queue: If not specified, the allocation will be freed
845
+ eagerly, irrespective of whether pending/enqueued operations
846
+ are still using this memory.
847
+
848
+ If specified, deallocation of the memory will be enqueued
849
+ with the given queue, and will only be performed
850
+ after previously-enqueue operations in the queue have
851
+ completed.
852
+
853
+ It is an error to specify an out-of-order queue.
854
+
855
+ .. warning::
856
+
857
+ Not specifying a queue will typically lead to undesired
858
+ behavior, including crashes and memory corruption.
859
+ See the warning in :ref:`svm`.
860
+
861
+ .. automethod:: enqueue_release
862
+
863
+ Enqueue the release of this allocation into *queue*.
864
+ If *queue* is not specified, enqueue the deallocation
865
+ into the queue provided at allocation time or via
866
+ :class:`bind_to_queue`.
867
+
868
+ .. automethod:: bind_to_queue
869
+
870
+ Change the queue used for implicit enqueue of deallocation
871
+ to *queue*. Sufficient synchronization is ensured by
872
+ enqueuing a marker into the old queue and waiting on this
873
+ marker in the new queue.
874
+
875
+ .. automethod:: unbind_from_queue
876
+
877
+ Configure the allocation to no longer implicitly enqueue
878
+ memory allocation. If such a queue was previously provided,
879
+ :meth:`~CommandQueue.finish` is automatically called on it.
880
+ """
881
+
882
+ # }}}
883
+
884
+ # {{{ SVM
885
+
886
+ if _cl.get_cl_header_version() >= (2, 0):
887
+ _cl.SVM.__doc__ = """Tags an object exhibiting the Python buffer interface
888
+ (such as a :class:`numpy.ndarray`) as referring to shared virtual
889
+ memory.
890
+
891
+ Is a :class:`SVMPointer`, hence objects of this type may be passed
892
+ to kernel calls and :func:`enqueue_copy`, and all methods declared
893
+ there are also available there. Note that :meth:`map` differs
894
+ slightly from :meth:`SVMPointer.map`.
895
+
896
+ Depending on the features of the OpenCL implementation, the following
897
+ types of objects may be passed to/wrapped in this type:
898
+
899
+ * fine-grain shared memory as returned by (e.g.) :func:`fsvm_empty`,
900
+ if the implementation supports fine-grained shared virtual memory.
901
+ This memory may directly be passed to a kernel::
902
+
903
+ ary = cl.fsvm_empty(ctx, 1000, np.float32)
904
+ assert isinstance(ary, np.ndarray)
905
+
906
+ prg.twice(queue, ary.shape, None, cl.SVM(ary))
907
+ queue.finish() # synchronize
908
+ print(ary) # access from host
909
+
910
+ Observe how mapping (as needed in coarse-grain SVM) is no longer
911
+ necessary.
912
+
913
+ * any :class:`numpy.ndarray` (or other Python object with a buffer
914
+ interface) if the implementation supports fine-grained *system*
915
+ shared virtual memory.
916
+
917
+ This is how plain :mod:`numpy` arrays may directly be passed to a
918
+ kernel::
919
+
920
+ ary = np.zeros(1000, np.float32)
921
+ prg.twice(queue, ary.shape, None, cl.SVM(ary))
922
+ queue.finish() # synchronize
923
+ print(ary) # access from host
924
+
925
+ * coarse-grain shared memory as returned by (e.g.) :func:`csvm_empty`
926
+ for any implementation of OpenCL 2.0.
927
+
928
+ .. note::
929
+
930
+ Applications making use of coarse-grain SVM may be better
931
+ served by opaque-style SVM. See :ref:`opaque-svm`.
932
+
933
+ This is how coarse-grain SVM may be used from both host and device::
934
+
935
+ svm_ary = cl.SVM(
936
+ cl.csvm_empty(ctx, 1000, np.float32, alignment=64))
937
+ assert isinstance(svm_ary.mem, np.ndarray)
938
+
939
+ with svm_ary.map_rw(queue) as ary:
940
+ ary.fill(17) # use from host
941
+
942
+ prg.twice(queue, svm_ary.mem.shape, None, svm_ary)
943
+
944
+ Coarse-grain shared-memory *must* be mapped into host address space
945
+ using :meth:`~SVMPointer.map` before being accessed through the
946
+ :mod:`numpy` interface.
947
+
948
+ .. note::
949
+
950
+ This object merely serves as a 'tag' that changes the behavior
951
+ of functions to which it is passed. It has no special management
952
+ relationship to the memory it tags. For example, it is permissible
953
+ to grab a :class:`numpy.ndarray` out of :attr:`SVM.mem` of one
954
+ :class:`SVM` instance and use the array to construct another.
955
+ Neither of the tags need to be kept alive.
956
+
957
+ .. versionadded:: 2016.2
958
+
959
+ .. attribute:: mem
960
+
961
+ The wrapped object.
962
+
963
+ .. automethod:: __init__
964
+ .. automethod:: map
965
+ .. automethod:: map_ro
966
+ .. automethod:: map_rw
967
+ """
968
+
969
+ # }}}
970
+
971
+ if _cl.get_cl_header_version() >= (2, 0):
972
+ _cl.SVM.map = svm_map
973
+ _cl.SVM.map_ro = svm_map_ro
974
+ _cl.SVM.map_rw = svm_map_rw
975
+ _cl.SVM._enqueue_unmap = svm__enqueue_unmap
976
+
977
+ # }}}
978
+
979
+ for cls in CONSTANT_CLASSES:
980
+ cls._is_bitfield = cls in BITFIELD_CONSTANT_CLASSES
981
+ cls.to_string = classmethod(to_string)
982
+
983
+
984
+ _add_functionality()
985
+
986
+
987
+ # ORDER DEPENDENCY: Some of the above may override get_info, the effect needs
988
+ # to be visible through the attributes. So get_info attr creation needs to happen
989
+ # after the overriding is complete.
990
+
991
+ T = TypeVar("T")
992
+
993
+
994
+ InfoT = TypeVar("InfoT")
995
+
996
+
997
+ def make_getinfo(
998
+ info_method: Callable[[T, InfoT], object],
999
+ info_constant: InfoT
1000
+ ) -> property:
1001
+ def result(self: T) -> object:
1002
+ return info_method(self, info_constant)
1003
+
1004
+ return property(result)
1005
+
1006
+
1007
+ def make_cacheable_getinfo(
1008
+ info_method: Callable[[T, InfoT], object],
1009
+ cache_attr: str,
1010
+ info_constant: InfoT
1011
+ ) -> property:
1012
+ def result(self: T):
1013
+ try:
1014
+ return getattr(self, cache_attr)
1015
+ except AttributeError:
1016
+ pass
1017
+
1018
+ result = info_method(self, info_constant)
1019
+ setattr(self, cache_attr, result)
1020
+ return result
1021
+
1022
+ return property(result)
1023
+
1024
+
1025
+ def add_get_info(
1026
+ cls: type[T],
1027
+ info_method: Callable[[T, InfoT], object],
1028
+ info_class: type[InfoT],
1029
+ cacheable_attrs: Collection[str] = (),
1030
+ ) -> None:
1031
+ for info_name, _info_value in info_class.__dict__.items():
1032
+ if info_name == "to_string" or info_name.startswith("_"):
1033
+ continue
1034
+
1035
+ info_lower = info_name.lower()
1036
+ info_constant = cast("InfoT", getattr(info_class, info_name))
1037
+ if info_name in cacheable_attrs:
1038
+ cache_attr = intern("_info_cache_"+info_lower)
1039
+ setattr(cls, info_lower, make_cacheable_getinfo(
1040
+ info_method, cache_attr, info_constant))
1041
+ else:
1042
+ setattr(cls, info_lower, make_getinfo(info_method, info_constant))
1043
+
1044
+ # }}}
1045
+
1046
+ if _cl.have_gl():
1047
+ def gl_object_get_gl_object(self):
1048
+ return self.get_gl_object_info()[1]
1049
+
1050
+ _cl.GLBuffer.gl_object = property(gl_object_get_gl_object)
1051
+ _cl.GLTexture.gl_object = property(gl_object_get_gl_object)
1052
+
1053
+
1054
+ def _add_all_get_info():
1055
+ add_get_info(_cl.Platform, _cl.Platform.get_info, _cl.platform_info)
1056
+ add_get_info(_cl.Device, _cl.Device.get_info, _cl.device_info,
1057
+ ["PLATFORM", "MAX_WORK_GROUP_SIZE", "MAX_COMPUTE_UNITS"])
1058
+ add_get_info(_cl.Context, _cl.Context.get_info, _cl.context_info)
1059
+ add_get_info(_cl.CommandQueue, _cl.CommandQueue.get_info, _cl.command_queue_info,
1060
+ ["CONTEXT", "DEVICE"])
1061
+ add_get_info(_cl.Event, _cl.Event.get_info, _cl.event_info)
1062
+ add_get_info(_cl.MemoryObjectHolder, _cl.MemoryObjectHolder.get_info, _cl.mem_info)
1063
+ add_get_info(_cl.Image, _cl.Image.get_image_info, _cl.image_info)
1064
+ add_get_info(_cl.Pipe, _cl.Pipe.get_pipe_info, _cl.pipe_info)
1065
+ add_get_info(_cl.Kernel, _cl.Kernel.get_info, _cl.kernel_info)
1066
+ add_get_info(_cl.Sampler, _cl.Sampler.get_info, _cl.sampler_info)
1067
+
1068
+
1069
+ _add_all_get_info()