pyopencl 2025.2.5__cp311-cp311-win_amd64.whl → 2025.2.7__cp311-cp311-win_amd64.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.

pyopencl/array.py CHANGED
@@ -1,7 +1,5 @@
1
1
  """CL device arrays."""
2
2
 
3
- # NOTE: for elwise_kernel_runner which adds keyword arguments
4
- # pylint:disable=unexpected-keyword-arg
5
3
  from __future__ import annotations
6
4
 
7
5
 
@@ -41,12 +39,14 @@ from typing import (
41
39
  Concatenate,
42
40
  Literal,
43
41
  ParamSpec,
42
+ TypeAlias,
43
+ TypeVar,
44
44
  cast,
45
45
  )
46
46
  from warnings import warn
47
47
 
48
48
  import numpy as np
49
- from typing_extensions import Self, override
49
+ from typing_extensions import Self, TypeIs, override
50
50
 
51
51
  import pyopencl as cl
52
52
  import pyopencl.elementwise as elementwise
@@ -76,6 +76,9 @@ else:
76
76
  _SVMPointer_or_nothing = ()
77
77
 
78
78
 
79
+ ArrayT = TypeVar("ArrayT", bound="Array")
80
+
81
+
79
82
  class _NoValue:
80
83
  pass
81
84
 
@@ -213,7 +216,7 @@ def _splay(
213
216
  group_count = max_groups
214
217
  work_items_per_group = max_work_items
215
218
 
216
- # print("n:%d gc:%d wipg:%d" % (n, group_count, work_items_per_group))
219
+ # print(f"n:{n} gc:{group_count} wipg:{work_items_per_group}")
217
220
  return (group_count*work_items_per_group,), (work_items_per_group,)
218
221
 
219
222
 
@@ -246,14 +249,13 @@ def elwise_kernel_runner(
246
249
  assert queue is not None
247
250
 
248
251
  knl = kernel_getter(out, *args, **kwargs)
249
- work_group_info = cast("int", knl.get_work_group_info(
252
+ gs, ls = out._get_sizes(queue, knl.get_work_group_info(
250
253
  cl.kernel_work_group_info.WORK_GROUP_SIZE,
251
254
  queue.device))
252
- gs, ls = out._get_sizes(queue, work_group_info)
253
255
 
254
256
  knl_args = (out, *args, out.size)
255
257
  if ARRAY_KERNEL_EXEC_HOOK is not None:
256
- return ARRAY_KERNEL_EXEC_HOOK( # pylint: disable=not-callable
258
+ return ARRAY_KERNEL_EXEC_HOOK(
257
259
  knl, queue, gs, ls, *knl_args, wait_for=wait_for)
258
260
  else:
259
261
  return knl(queue, gs, ls, *knl_args, wait_for=wait_for)
@@ -289,6 +291,12 @@ _ARRAY_GET_SIZES_CACHE: \
289
291
  _BOOL_DTYPE = np.dtype(np.int8)
290
292
  _NOT_PRESENT = object()
291
293
 
294
+ ScalarLike: TypeAlias = int | float | complex | np.number[Any]
295
+
296
+
297
+ def _is_scalar(s: object) -> TypeIs[ScalarLike]:
298
+ return isinstance(s, SCALAR_CLASSES)
299
+
292
300
 
293
301
  class Array:
294
302
  """A :class:`numpy.ndarray` work-alike that stores its data and performs
@@ -478,16 +486,6 @@ class Array:
478
486
 
479
487
  __array_priority__: ClassVar[int] = 100
480
488
 
481
- queue: cl.CommandQueue | None
482
- shape: tuple[int, ...]
483
- dtype: np.dtype[Any]
484
- strides: tuple[int, ...]
485
- events: list[cl.Event]
486
- nbytes: int
487
- size: int
488
- allocator: Allocator | None
489
- base_data: cl.MemoryObjectHolder | cl.SVMPointer | None
490
-
491
489
  def __init__(
492
490
  self,
493
491
  cq: cl.Context | cl.CommandQueue | None,
@@ -568,7 +566,7 @@ class Array:
568
566
  dtype = np.dtype(dtype)
569
567
 
570
568
  try:
571
- shape = tuple(shape) # type: ignore[arg-type]
569
+ shape = tuple(shape)
572
570
  except TypeError as err:
573
571
  if not isinstance(shape, (int, np.integer)):
574
572
  raise TypeError(
@@ -587,7 +585,7 @@ class Array:
587
585
  raise ValueError(f"negative dimensions are not allowed: {shape}")
588
586
  if np.any([np.array([s]).dtype.kind not in ["u", "i"] for s in shape]):
589
587
  raise ValueError(
590
- "Invalid shape %s ; dimensions, must be integer" % (str(shape)))
588
+ f"invalid shape {shape} (all dimensions must be integers)")
591
589
  size = np.prod(shape_array, dtype=np.uint64).item()
592
590
 
593
591
  if strides is None:
@@ -624,34 +622,37 @@ class Array:
624
622
  if alloc_nbytes < 0:
625
623
  raise ValueError("cannot allocate CL buffer with negative size")
626
624
 
627
- self.queue = queue
628
- self.shape = shape
629
- self.dtype = dtype
630
- self.strides = strides
631
- self.events = [] if events is None else events
632
- self.nbytes = alloc_nbytes
633
- self.size = size
634
- self.allocator = allocator
635
-
625
+ base_data = None
636
626
  if data is None:
637
627
  if alloc_nbytes == 0:
638
- self.base_data = None
628
+ base_data = None
639
629
 
640
630
  else:
641
- if self.allocator is None:
631
+ if allocator is None:
642
632
  if context is None and queue is not None:
643
633
  context = queue.context
644
634
 
645
- self.base_data = cl.Buffer(
646
- context, cl.mem_flags.READ_WRITE, alloc_nbytes)
635
+ assert context is not None
636
+ base_data = cl.Buffer(
637
+ context, cl.mem_flags.READ_WRITE, alloc_nbytes)
647
638
  else:
648
- self.base_data = self.allocator(alloc_nbytes)
639
+ base_data = allocator(alloc_nbytes)
649
640
  else:
650
- self.base_data = data
651
-
652
- self.offset = offset
653
- self.context = context
654
- self._flags = _flags
641
+ base_data = data
642
+
643
+ self.queue: cl.CommandQueue | None = queue
644
+ self.context: cl.Context | None = context
645
+ self.shape: tuple[int, ...] = shape
646
+ self.dtype: np.dtype[Any] = dtype
647
+ self.strides: tuple[int, ...] = strides
648
+ self.events: list[cl.Event] = [] if events is None else events
649
+ self.nbytes: int = alloc_nbytes
650
+ self.size: int = size
651
+ self.allocator: Allocator | None = allocator
652
+ self.base_data: cl.MemoryObjectHolder | cl.SVMPointer | None = base_data
653
+ self.offset: int = offset
654
+
655
+ self._flags: _ArrayFlags | None = _flags
655
656
 
656
657
  if __debug__:
657
658
  if queue is not None and isinstance(
@@ -664,11 +665,11 @@ class Array:
664
665
  InconsistentOpenCLQueueWarning, stacklevel=2)
665
666
 
666
667
  @property
667
- def ndim(self):
668
+ def ndim(self) -> int:
668
669
  return len(self.shape)
669
670
 
670
671
  @property
671
- def data(self):
672
+ def data(self) -> cl.MemoryObjectHolder | cl.SVMPointer | None:
672
673
  if self.offset:
673
674
  raise ArrayHasOffsetError()
674
675
  else:
@@ -679,6 +680,7 @@ class Array:
679
680
  f = self._flags
680
681
  if f is None:
681
682
  self._flags = f = _ArrayFlags(self)
683
+
682
684
  return f
683
685
 
684
686
  def _new_with_changes(self,
@@ -899,14 +901,16 @@ class Array:
899
901
 
900
902
  return result
901
903
 
902
- def __str__(self):
904
+ @override
905
+ def __str__(self) -> str:
903
906
  if self.queue is None:
904
907
  return (f"<cl.{type(self).__name__} {self.shape} of {self.dtype} "
905
908
  "without queue, call with_queue()>")
906
909
 
907
910
  return str(self.get())
908
911
 
909
- def __repr__(self):
912
+ @override
913
+ def __repr__(self) -> str:
910
914
  if self.queue is None:
911
915
  return (f"<cl.{type(self).__name__} {self.shape} of {self.dtype} "
912
916
  f"at {id(self):x} without queue, call with_queue()>")
@@ -922,49 +926,69 @@ class Array:
922
926
 
923
927
  return result
924
928
 
925
- def safely_stringify_for_pudb(self):
929
+ def safely_stringify_for_pudb(self) -> str:
926
930
  return f"cl.{type(self).__name__} {self.dtype} {self.shape}"
927
931
 
928
- def __hash__(self):
932
+ @override
933
+ def __hash__(self) -> int:
929
934
  raise TypeError("pyopencl arrays are not hashable.")
930
935
 
931
936
  # {{{ kernel invocation wrappers
932
937
 
933
938
  @staticmethod
934
939
  @elwise_kernel_runner
935
- def _axpbyz(out, afac, a, bfac, b, queue: cl.CommandQueue | None = None):
936
- """Compute ``out = selffac * self + otherfac*other``,
940
+ def _axpbyz(out: Array,
941
+ a: ScalarLike,
942
+ x: Array,
943
+ b: ScalarLike,
944
+ y: Array,
945
+ queue: cl.CommandQueue | None = None) -> cl.Kernel:
946
+ """Compute ``out = a*x + b*y``,
937
947
  where *other* is an array."""
938
- a_shape = a.shape
939
- b_shape = b.shape
948
+ x_shape = x.shape
949
+ y_shape = y.shape
940
950
  out_shape = out.shape
941
- assert (a_shape == b_shape == out_shape
942
- or (a_shape == () and b_shape == out_shape)
943
- or (b_shape == () and a_shape == out_shape))
951
+
952
+ assert out.context is not None
953
+ assert (x_shape == y_shape == out_shape
954
+ or (x_shape == () and y_shape == out_shape)
955
+ or (y_shape == () and x_shape == out_shape))
956
+
944
957
  return elementwise.get_axpbyz_kernel(
945
- out.context, a.dtype, b.dtype, out.dtype,
946
- x_is_scalar=(a_shape == ()),
947
- y_is_scalar=(b_shape == ()))
958
+ out.context, x.dtype, y.dtype, out.dtype,
959
+ x_is_scalar=(x_shape == ()),
960
+ y_is_scalar=(y_shape == ()))
948
961
 
949
962
  @staticmethod
950
963
  @elwise_kernel_runner
951
- def _axpbz(out, a, x, b, queue: cl.CommandQueue | None = None):
964
+ def _axpbz(out: Array,
965
+ a: ScalarLike,
966
+ x: Array,
967
+ b: ScalarLike,
968
+ queue: cl.CommandQueue | None = None) -> cl.Kernel:
952
969
  """Compute ``z = a * x + b``, where *b* is a scalar."""
953
- a = np.array(a)
954
- b = np.array(b)
955
970
  assert out.shape == x.shape
956
- return elementwise.get_axpbz_kernel(out.context,
957
- a.dtype, x.dtype, b.dtype, out.dtype)
971
+ assert out.context is not None
972
+
973
+ return elementwise.get_axpbz_kernel(
974
+ out.context,
975
+ np.array(a).dtype, x.dtype, np.array(b).dtype, out.dtype)
958
976
 
959
977
  @staticmethod
960
978
  @elwise_kernel_runner
961
- def _elwise_multiply(out, a, b, queue: cl.CommandQueue | None = None):
979
+ def _elwise_multiply(out: Array,
980
+ a: Array,
981
+ b: Array,
982
+ queue: cl.CommandQueue | None = None) -> cl.Kernel:
962
983
  a_shape = a.shape
963
984
  b_shape = b.shape
964
985
  out_shape = out.shape
986
+
965
987
  assert (a_shape == b_shape == out_shape
966
988
  or (a_shape == () and b_shape == out_shape)
967
989
  or (b_shape == () and a_shape == out_shape))
990
+ assert a.context is not None
991
+
968
992
  return elementwise.get_multiply_kernel(
969
993
  a.context, a.dtype, b.dtype, out.dtype,
970
994
  x_is_scalar=(a_shape == ()),
@@ -973,19 +997,27 @@ class Array:
973
997
 
974
998
  @staticmethod
975
999
  @elwise_kernel_runner
976
- def _rdiv_scalar(out, ary, other, queue: cl.CommandQueue | None = None):
977
- other = np.array(other)
1000
+ def _rdiv_scalar(out: Array,
1001
+ ary: Array,
1002
+ other: ScalarLike,
1003
+ queue: cl.CommandQueue | None = None) -> cl.Kernel:
1004
+ assert out.context is not None
978
1005
  assert out.shape == ary.shape
1006
+
979
1007
  return elementwise.get_rdivide_elwise_kernel(
980
- out.context, ary.dtype, other.dtype, out.dtype)
1008
+ out.context, ary.dtype, np.array(other).dtype, out.dtype)
981
1009
 
982
1010
  @staticmethod
983
1011
  @elwise_kernel_runner
984
- def _div(out, self, other, queue: cl.CommandQueue | None = None):
1012
+ def _div(out: Array,
1013
+ self: Array,
1014
+ other: Array,
1015
+ queue: cl.CommandQueue | None = None) -> cl.Kernel:
985
1016
  """Divides an array by another array."""
986
1017
  assert (self.shape == other.shape == out.shape
987
1018
  or (self.shape == () and other.shape == out.shape)
988
1019
  or (other.shape == () and self.shape == out.shape))
1020
+ assert self.context is not None
989
1021
 
990
1022
  return elementwise.get_divide_kernel(self.context,
991
1023
  self.dtype, other.dtype, out.dtype,
@@ -994,15 +1026,18 @@ class Array:
994
1026
 
995
1027
  @staticmethod
996
1028
  @elwise_kernel_runner
997
- def _fill(result, scalar):
1029
+ def _fill(result: Array, scalar: ScalarLike) -> cl.Kernel:
1030
+ assert result.context is not None
998
1031
  return elementwise.get_fill_kernel(result.context, result.dtype)
999
1032
 
1000
1033
  @staticmethod
1001
1034
  @elwise_kernel_runner
1002
- def _abs(result, arg):
1035
+ def _abs(result: Array, arg: Array) -> cl.Kernel:
1036
+ assert arg.context is not None
1037
+
1003
1038
  if arg.dtype.kind == "c":
1004
1039
  from pyopencl.elementwise import complex_dtype_to_name
1005
- fname = "%s_abs" % complex_dtype_to_name(arg.dtype)
1040
+ fname = f"{complex_dtype_to_name(arg.dtype)}_abs"
1006
1041
  elif arg.dtype.kind == "f":
1007
1042
  fname = "fabs"
1008
1043
  elif arg.dtype.kind in ["u", "i"]:
@@ -1015,63 +1050,83 @@ class Array:
1015
1050
 
1016
1051
  @staticmethod
1017
1052
  @elwise_kernel_runner
1018
- def _real(result, arg):
1053
+ def _real(result: Array, arg: Array) -> cl.Kernel:
1019
1054
  from pyopencl.elementwise import complex_dtype_to_name
1020
- fname = "%s_real" % complex_dtype_to_name(arg.dtype)
1055
+
1056
+ assert arg.context is not None
1057
+ fname = f"{complex_dtype_to_name(arg.dtype)}_real"
1058
+
1021
1059
  return elementwise.get_unary_func_kernel(
1022
1060
  arg.context, fname, arg.dtype, out_dtype=result.dtype)
1023
1061
 
1024
1062
  @staticmethod
1025
1063
  @elwise_kernel_runner
1026
- def _imag(result, arg):
1064
+ def _imag(result: Array, arg: Array) -> cl.Kernel:
1027
1065
  from pyopencl.elementwise import complex_dtype_to_name
1028
- fname = "%s_imag" % complex_dtype_to_name(arg.dtype)
1066
+
1067
+ assert arg.context is not None
1068
+ fname = f"{complex_dtype_to_name(arg.dtype)}_imag"
1069
+
1029
1070
  return elementwise.get_unary_func_kernel(
1030
1071
  arg.context, fname, arg.dtype, out_dtype=result.dtype)
1031
1072
 
1032
1073
  @staticmethod
1033
1074
  @elwise_kernel_runner
1034
- def _conj(result, arg):
1075
+ def _conj(result: Array, arg: Array) -> cl.Kernel:
1035
1076
  from pyopencl.elementwise import complex_dtype_to_name
1036
- fname = "%s_conj" % complex_dtype_to_name(arg.dtype)
1077
+
1078
+ assert arg.context is not None
1079
+ fname = f"{complex_dtype_to_name(arg.dtype)}_conj"
1080
+
1037
1081
  return elementwise.get_unary_func_kernel(
1038
1082
  arg.context, fname, arg.dtype, out_dtype=result.dtype)
1039
1083
 
1040
1084
  @staticmethod
1041
1085
  @elwise_kernel_runner
1042
- def _pow_scalar(result, ary, exponent):
1043
- exponent = np.array(exponent)
1086
+ def _pow_scalar(result: Array,
1087
+ ary: Array,
1088
+ exponent: ScalarLike) -> cl.Kernel:
1089
+ assert result.context is not None
1044
1090
  return elementwise.get_pow_kernel(result.context,
1045
- ary.dtype, exponent.dtype, result.dtype,
1091
+ ary.dtype, np.array(exponent).dtype, result.dtype,
1046
1092
  is_base_array=True, is_exp_array=False)
1047
1093
 
1048
1094
  @staticmethod
1049
1095
  @elwise_kernel_runner
1050
- def _rpow_scalar(result, base, exponent):
1051
- base = np.array(base)
1096
+ def _rpow_scalar(result: Array,
1097
+ base: ScalarLike,
1098
+ exponent: Array) -> cl.Kernel:
1099
+ assert result.context is not None
1052
1100
  return elementwise.get_pow_kernel(result.context,
1053
- base.dtype, exponent.dtype, result.dtype,
1101
+ np.array(base).dtype, exponent.dtype, result.dtype,
1054
1102
  is_base_array=False, is_exp_array=True)
1055
1103
 
1056
1104
  @staticmethod
1057
1105
  @elwise_kernel_runner
1058
- def _pow_array(result, base, exponent):
1106
+ def _pow_array(result: Array,
1107
+ base: Array,
1108
+ exponent: Array) -> cl.Kernel:
1109
+ assert result.context is not None
1059
1110
  return elementwise.get_pow_kernel(
1060
1111
  result.context, base.dtype, exponent.dtype, result.dtype,
1061
1112
  is_base_array=True, is_exp_array=True)
1062
1113
 
1063
1114
  @staticmethod
1064
1115
  @elwise_kernel_runner
1065
- def _reverse(result, ary):
1116
+ def _reverse(result: Array, ary: Array) -> cl.Kernel:
1117
+ assert result.context is not None
1066
1118
  return elementwise.get_reverse_kernel(result.context, ary.dtype)
1067
1119
 
1068
1120
  @staticmethod
1069
1121
  @elwise_kernel_runner
1070
- def _copy(dest, src):
1122
+ def _copy(dest: Array, src: Array) -> cl.Kernel:
1123
+ assert dest.context is not None
1071
1124
  return elementwise.get_copy_kernel(
1072
1125
  dest.context, dest.dtype, src.dtype)
1073
1126
 
1074
- def _new_like_me(self, dtype=None, queue: cl.CommandQueue | None = None):
1127
+ def _new_like_me(self,
1128
+ dtype: DTypeLike = None,
1129
+ queue: cl.CommandQueue | None = None) -> Self:
1075
1130
  if dtype is None:
1076
1131
  dtype = self.dtype
1077
1132
  strides = self.strides
@@ -1095,20 +1150,26 @@ class Array:
1095
1150
 
1096
1151
  @staticmethod
1097
1152
  @elwise_kernel_runner
1098
- def _scalar_binop(out, a, b, queue: cl.CommandQueue | None = None, op=None):
1153
+ def _scalar_binop(out: Array, a: Array, b: ScalarLike, op: str,
1154
+ queue: cl.CommandQueue | None = None) -> cl.Kernel:
1155
+ assert out.context is not None
1099
1156
  return elementwise.get_array_scalar_binop_kernel(
1100
1157
  out.context, op, out.dtype, a.dtype,
1101
1158
  np.array(b).dtype)
1102
1159
 
1103
1160
  @staticmethod
1104
1161
  @elwise_kernel_runner
1105
- def _array_binop(out, a, b, queue: cl.CommandQueue | None = None, op=None):
1162
+ def _array_binop(out: Array, a: Array, b: Array, op: str,
1163
+ queue: cl.CommandQueue | None = None) -> cl.Kernel:
1106
1164
  a_shape = a.shape
1107
1165
  b_shape = b.shape
1108
1166
  out_shape = out.shape
1167
+
1109
1168
  assert (a_shape == b_shape == out_shape
1110
1169
  or (a_shape == () and b_shape == out_shape)
1111
1170
  or (b_shape == () and a_shape == out_shape))
1171
+ assert out.context is not None
1172
+
1112
1173
  return elementwise.get_array_binop_kernel(
1113
1174
  out.context, op, out.dtype, a.dtype, b.dtype,
1114
1175
  a_is_scalar=(a_shape == ()),
@@ -1116,9 +1177,12 @@ class Array:
1116
1177
 
1117
1178
  @staticmethod
1118
1179
  @elwise_kernel_runner
1119
- def _unop(out, a, queue: cl.CommandQueue | None = None, op=None):
1180
+ def _unop(out: Array, a: Array, op: str,
1181
+ queue: cl.CommandQueue | None = None) -> cl.Kernel:
1182
+ assert out.context is not None
1120
1183
  if out.shape != a.shape:
1121
1184
  raise ValueError("shapes of arguments do not match")
1185
+
1122
1186
  return elementwise.get_unop_kernel(
1123
1187
  out.context, op, a.dtype, out.dtype)
1124
1188
 
@@ -1590,7 +1654,7 @@ class Array:
1590
1654
  result.add_event(self._reverse(result, self))
1591
1655
  return result
1592
1656
 
1593
- def astype(self, dtype, queue: cl.CommandQueue | None = None):
1657
+ def astype(self, dtype: DTypeLike, queue: cl.CommandQueue | None = None):
1594
1658
  """Return a copy of *self*, cast to *dtype*."""
1595
1659
  if dtype == self.dtype:
1596
1660
  return self.copy()
@@ -1636,15 +1700,26 @@ class Array:
1636
1700
 
1637
1701
  @staticmethod
1638
1702
  @elwise_kernel_runner
1639
- def _scalar_comparison(out, a, b, queue: cl.CommandQueue | None = None, op=None):
1703
+ def _scalar_comparison(out: Array,
1704
+ a: Array,
1705
+ b: ScalarLike,
1706
+ op: str,
1707
+ queue: cl.CommandQueue | None = None) -> cl.Kernel:
1708
+ assert out.context is not None
1640
1709
  return elementwise.get_array_scalar_comparison_kernel(
1641
1710
  out.context, op, a.dtype)
1642
1711
 
1643
1712
  @staticmethod
1644
1713
  @elwise_kernel_runner
1645
- def _array_comparison(out, a, b, queue: cl.CommandQueue | None = None, op=None):
1714
+ def _array_comparison(out: Array,
1715
+ a: Array,
1716
+ b: Array,
1717
+ op: str,
1718
+ queue: cl.CommandQueue | None = None) -> cl.Kernel:
1719
+ assert out.context is not None
1646
1720
  if a.shape != b.shape:
1647
1721
  raise ValueError("shapes of comparison arguments do not match")
1722
+
1648
1723
  return elementwise.get_array_comparison_kernel(
1649
1724
  out.context, op, a.dtype, b.dtype)
1650
1725
 
@@ -1810,8 +1885,7 @@ class Array:
1810
1885
 
1811
1886
  order = kwargs.pop("order", "C")
1812
1887
  if kwargs:
1813
- raise TypeError("unexpected keyword arguments: %s"
1814
- % list(kwargs.keys()))
1888
+ raise TypeError(f"unexpected keyword arguments: {list(kwargs)}")
1815
1889
 
1816
1890
  if order not in "CF":
1817
1891
  raise ValueError("order must be either 'C' or 'F'")
@@ -2123,8 +2197,7 @@ class Array:
2123
2197
  index_entry += array_shape
2124
2198
 
2125
2199
  if not (0 <= index_entry < array_shape):
2126
- raise IndexError(
2127
- "subindex in axis %d out of range" % index_axis)
2200
+ raise IndexError(f"subindex in axis {index_axis} out of range")
2128
2201
 
2129
2202
  new_offset += self.strides[array_axis]*index_entry
2130
2203
 
@@ -2154,7 +2227,7 @@ class Array:
2154
2227
  index_axis += 1
2155
2228
 
2156
2229
  else:
2157
- raise IndexError("invalid subindex in axis %d" % index_axis)
2230
+ raise IndexError(f"invalid subindex in axis {index_axis}")
2158
2231
 
2159
2232
  while array_axis < len(self.shape):
2160
2233
  new_shape.append(self.shape[array_axis])
@@ -2355,7 +2428,7 @@ def empty_like(
2355
2428
  allocator=allocator)
2356
2429
 
2357
2430
 
2358
- def zeros_like(ary):
2431
+ def zeros_like(ary: ArrayT) -> ArrayT:
2359
2432
  """Make a new, zero-initialized :class:`Array` having the same properties
2360
2433
  as *other_ary*.
2361
2434
  """
@@ -2656,11 +2729,15 @@ def multi_put(
2656
2729
  return []
2657
2730
 
2658
2731
  from pytools import single_valued
2732
+
2659
2733
  a_dtype = single_valued(a.dtype for a in arrays)
2660
2734
  a_allocator = arrays[0].allocator
2735
+
2661
2736
  context = dest_indices.context
2662
2737
  queue = queue or dest_indices.queue
2663
2738
  assert queue is not None
2739
+ assert context is not None
2740
+
2664
2741
  if wait_for is None:
2665
2742
  wait_for = []
2666
2743
  wait_for = [*wait_for, *dest_indices.events]
@@ -2764,7 +2841,6 @@ def concatenate(arrays, axis=0, queue: cl.CommandQueue | None = None, allocator=
2764
2841
  raise ValueError(
2765
2842
  f"{i_ary}-th array has residual not matching other arrays")
2766
2843
 
2767
- # pylint: disable=unsupported-assignment-operation
2768
2844
  shape[axis] += ary.shape[axis]
2769
2845
 
2770
2846
  # }}}
@@ -2960,18 +3036,18 @@ def _if_positive(result, criterion, then_, else_):
2960
3036
 
2961
3037
 
2962
3038
  def if_positive(
2963
- criterion,
2964
- then_,
2965
- else_,
2966
- out=None,
3039
+ criterion: Array | ScalarLike,
3040
+ then_: Array | ScalarLike,
3041
+ else_: Array | ScalarLike,
3042
+ out: Array | None = None,
2967
3043
  queue: cl.CommandQueue | None = None):
2968
3044
  """Return an array like *then_*, which, for the element at index *i*,
2969
3045
  contains *then_[i]* if *criterion[i]>0*, else *else_[i]*.
2970
3046
  """
2971
3047
 
2972
- is_then_scalar = isinstance(then_, SCALAR_CLASSES)
2973
- is_else_scalar = isinstance(else_, SCALAR_CLASSES)
2974
- if isinstance(criterion, SCALAR_CLASSES) and is_then_scalar and is_else_scalar:
3048
+ is_then_scalar = _is_scalar(then_)
3049
+ is_else_scalar = _is_scalar(else_)
3050
+ if _is_scalar(criterion) and is_then_scalar and is_else_scalar:
2975
3051
  result = np.where(criterion, then_, else_)
2976
3052
 
2977
3053
  if out is not None:
@@ -2981,53 +3057,60 @@ def if_positive(
2981
3057
  return result
2982
3058
 
2983
3059
  if is_then_scalar:
2984
- then_ = np.array(then_)
3060
+ then_ary = np.array(then_)
3061
+ else:
3062
+ then_ary = then_
3063
+
3064
+ assert not _is_scalar(criterion)
2985
3065
 
2986
3066
  if is_else_scalar:
2987
- else_ = np.array(else_)
3067
+ else_ary = np.array(else_)
3068
+ else:
3069
+ else_ary = else_
2988
3070
 
2989
- if then_.dtype != else_.dtype:
3071
+ if then_ary.dtype != else_ary.dtype:
2990
3072
  raise ValueError(
2991
- f"dtypes do not match: then_ is '{then_.dtype}' and "
2992
- f"else_ is '{else_.dtype}'")
3073
+ f"dtypes do not match: then_ary is '{then_ary.dtype}' and "
3074
+ f"else_ary is '{else_ary.dtype}'")
2993
3075
 
2994
- if then_.shape == () and else_.shape == ():
3076
+ if then_ary.shape == () and else_ary.shape == ():
2995
3077
  pass
2996
- elif then_.shape != () and else_.shape != ():
2997
- if not (criterion.shape == then_.shape == else_.shape):
3078
+ elif then_ary.shape != () and else_ary.shape != ():
3079
+ if not (criterion.shape == then_ary.shape == else_ary.shape):
2998
3080
  raise ValueError(
2999
3081
  f"shapes do not match: 'criterion' has shape {criterion.shape}"
3000
- f", 'then_' has shape {then_.shape} and 'else_' has shape "
3001
- f"{else_.shape}")
3002
- elif then_.shape == ():
3003
- if criterion.shape != else_.shape:
3082
+ f", 'then_ary' has shape {then_ary.shape} and 'else_ary' has shape "
3083
+ f"{else_ary.shape}")
3084
+ elif then_ary.shape == ():
3085
+ if criterion.shape != else_ary.shape:
3004
3086
  raise ValueError(
3005
3087
  f"shapes do not match: 'criterion' has shape {criterion.shape}"
3006
- f" and 'else_' has shape {else_.shape}")
3007
- elif else_.shape == ():
3008
- if criterion.shape != then_.shape:
3088
+ f" and 'else_ary' has shape {else_ary.shape}")
3089
+ elif else_ary.shape == ():
3090
+ if criterion.shape != then_ary.shape:
3009
3091
  raise ValueError(
3010
3092
  f"shapes do not match: 'criterion' has shape {criterion.shape}"
3011
- f" and 'then_' has shape {then_.shape}")
3093
+ f" and 'then_ary' has shape {then_ary.shape}")
3012
3094
  else:
3013
3095
  raise AssertionError()
3014
3096
 
3015
3097
  if out is None:
3016
- if then_.shape != ():
3098
+ if then_ary.shape != ():
3099
+ assert isinstance(then_ary, Array)
3017
3100
  out = empty_like(
3018
- then_, criterion.queue, allocator=criterion.allocator)
3101
+ then_ary, criterion.queue, allocator=criterion.allocator)
3019
3102
  else:
3020
3103
  # Use same strides as criterion
3021
3104
  cr_byte_strides = np.array(criterion.strides, dtype=np.int64)
3022
3105
  cr_item_strides = cr_byte_strides // criterion.dtype.itemsize
3023
- out_strides = tuple(cr_item_strides*then_.dtype.itemsize)
3106
+ out_strides = tuple(cr_item_strides*then_ary.dtype.itemsize)
3024
3107
 
3025
3108
  out = type(criterion)(
3026
- criterion.queue, criterion.shape, then_.dtype,
3109
+ criterion.queue, criterion.shape, then_ary.dtype,
3027
3110
  allocator=criterion.allocator,
3028
3111
  strides=out_strides)
3029
3112
 
3030
- event1 = _if_positive(out, criterion, then_, else_, queue=queue)
3113
+ event1 = _if_positive(out, criterion, then_ary, else_ary, queue=queue)
3031
3114
  out.add_event(event1)
3032
3115
 
3033
3116
  return out
@@ -3105,7 +3188,11 @@ def minimum(a, b, out=None, queue: cl.CommandQueue | None = None):
3105
3188
 
3106
3189
  # {{{ logical ops
3107
3190
 
3108
- def _logical_op(x1, x2, out, operator, queue: cl.CommandQueue | None = None):
3191
+ def _logical_op(x1: Array | ScalarLike,
3192
+ x2: Array | ScalarLike,
3193
+ out: Array | None,
3194
+ operator: str,
3195
+ queue: cl.CommandQueue | None = None) -> Array:
3109
3196
  # NOTE: Copied from pycuda.gpuarray
3110
3197
  assert operator in ["&&", "||"]
3111
3198
 
@@ -3129,6 +3216,7 @@ def _logical_op(x1, x2, out, operator, queue: cl.CommandQueue | None = None):
3129
3216
 
3130
3217
  out = out or ary_arg._new_like_me(dtype=np.int8)
3131
3218
 
3219
+ assert queue is not None
3132
3220
  assert out.shape == ary_arg.shape and out.dtype == np.int8
3133
3221
 
3134
3222
  knl = elementwise.get_array_scalar_binop_kernel(
@@ -3153,6 +3241,7 @@ def _logical_op(x1, x2, out, operator, queue: cl.CommandQueue | None = None):
3153
3241
  out = empty(queue, allocator=allocator,
3154
3242
  shape=x1.shape, dtype=np.int8)
3155
3243
 
3244
+ assert queue is not None
3156
3245
  assert out.shape == x1.shape and out.dtype == np.int8
3157
3246
 
3158
3247
  knl = elementwise.get_array_binop_kernel(