gpuarray 0.1.0__py3-none-any.whl → 0.2.0__py3-none-any.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.
gpuarray/__init__.py CHANGED
@@ -20,6 +20,13 @@ from .api import (
20
20
  relu,
21
21
  sigmoid,
22
22
  tanh,
23
+ where,
24
+ clip,
25
+ argmax,
26
+ argmin,
27
+ concatenate,
28
+ stack,
29
+ random,
23
30
  )
24
31
 
25
32
  __version__ = "0.1.0"
@@ -44,4 +51,11 @@ __all__ = [
44
51
  "relu",
45
52
  "sigmoid",
46
53
  "tanh",
54
+ "where",
55
+ "clip",
56
+ "argmax",
57
+ "argmin",
58
+ "concatenate",
59
+ "stack",
60
+ "random",
47
61
  ]
gpuarray/api.py CHANGED
@@ -97,3 +97,86 @@ def sigmoid(a: GPUArray) -> GPUArray:
97
97
 
98
98
  def tanh(a: GPUArray) -> GPUArray:
99
99
  return a._run_unary_op(shaders.TANH_SHADER)
100
+
101
+
102
+ # ----- new operations -----
103
+
104
+ def where(condition: GPUArray, x: GPUArray, y: GPUArray) -> GPUArray:
105
+ """Conditional select: where condition is 1.0, pick x; else pick y."""
106
+ import wgpu
107
+ from .device import get_device
108
+ from .core import _get_pipeline
109
+
110
+ assert condition.size == x.size == y.size
111
+ device = get_device()
112
+ _, pipeline = _get_pipeline(shaders.WHERE_SHADER)
113
+
114
+ out_buf = device.create_buffer(
115
+ size=condition.size * 4,
116
+ usage=wgpu.BufferUsage.STORAGE | wgpu.BufferUsage.COPY_SRC | wgpu.BufferUsage.COPY_DST,
117
+ )
118
+ bind_group = device.create_bind_group(
119
+ layout=pipeline.get_bind_group_layout(0),
120
+ entries=[
121
+ {"binding": 0, "resource": {"buffer": condition._buffer, "offset": 0, "size": condition.size * 4}},
122
+ {"binding": 1, "resource": {"buffer": x._buffer, "offset": 0, "size": x.size * 4}},
123
+ {"binding": 2, "resource": {"buffer": y._buffer, "offset": 0, "size": y.size * 4}},
124
+ {"binding": 3, "resource": {"buffer": out_buf, "offset": 0, "size": condition.size * 4}},
125
+ ],
126
+ )
127
+ import math
128
+ n_workgroups = math.ceil(condition.size / 256)
129
+ encoder = device.create_command_encoder()
130
+ pass_enc = encoder.begin_compute_pass()
131
+ pass_enc.set_pipeline(pipeline)
132
+ pass_enc.set_bind_group(0, bind_group)
133
+ pass_enc.dispatch_workgroups(n_workgroups)
134
+ pass_enc.end()
135
+ device.queue.submit([encoder.finish()])
136
+ return GPUArray(out_buf, condition._shape)
137
+
138
+
139
+ def clip(a: GPUArray, min_val: float, max_val: float) -> GPUArray:
140
+ """Clamp values to [min_val, max_val]."""
141
+ return a.clip(min_val, max_val)
142
+
143
+
144
+ def argmax(a: GPUArray) -> int:
145
+ """Return index of maximum value."""
146
+ return a.argmax()
147
+
148
+
149
+ def argmin(a: GPUArray) -> int:
150
+ """Return index of minimum value."""
151
+ return a.argmin()
152
+
153
+
154
+ def concatenate(arrays, axis: int = 0) -> GPUArray:
155
+ """Join arrays along an axis (CPU-side, creates new GPU buffer)."""
156
+ np_arrays = [a.to_numpy() for a in arrays]
157
+ result = np.concatenate(np_arrays, axis=axis)
158
+ return _from_numpy(result)
159
+
160
+
161
+ def stack(arrays, axis: int = 0) -> GPUArray:
162
+ """Stack arrays along a new axis (CPU-side, creates new GPU buffer)."""
163
+ np_arrays = [a.to_numpy() for a in arrays]
164
+ result = np.stack(np_arrays, axis=axis)
165
+ return _from_numpy(result)
166
+
167
+
168
+ # ----- random submodule -----
169
+
170
+ class _Random:
171
+ """Random array generation (CPU-side, uploaded to GPU)."""
172
+
173
+ def rand(self, *shape) -> GPUArray:
174
+ """Uniform random values in [0, 1)."""
175
+ return _from_numpy(np.random.rand(*shape).astype(np.float32))
176
+
177
+ def randn(self, *shape) -> GPUArray:
178
+ """Standard normal random values."""
179
+ return _from_numpy(np.random.randn(*shape).astype(np.float32))
180
+
181
+
182
+ random = _Random()
gpuarray/core.py CHANGED
@@ -343,6 +343,105 @@ class GPUArray:
343
343
  raise ValueError(f"Cannot reshape array of size {self.size} into shape {new_shape}")
344
344
  return GPUArray(self._buffer, new_shape)
345
345
 
346
+ # ----- clamp / clip -----
347
+
348
+ def clip(self, min_val: float, max_val: float) -> "GPUArray":
349
+ """Clamp values to [min_val, max_val]."""
350
+ device = get_device()
351
+ _, pipeline = _get_pipeline(shaders.CLAMP_SHADER)
352
+
353
+ out_buf = device.create_buffer(
354
+ size=self.size * 4,
355
+ usage=wgpu.BufferUsage.STORAGE | wgpu.BufferUsage.COPY_SRC | wgpu.BufferUsage.COPY_DST,
356
+ )
357
+ # Pack two f32 values, padded to 16 bytes
358
+ params_data = struct.pack("ff", min_val, max_val) + b"\x00" * 8
359
+ uniform_buf = device.create_buffer_with_data(
360
+ data=params_data,
361
+ usage=wgpu.BufferUsage.UNIFORM,
362
+ )
363
+ bind_group = device.create_bind_group(
364
+ layout=pipeline.get_bind_group_layout(0),
365
+ entries=[
366
+ {"binding": 0, "resource": {"buffer": self._buffer, "offset": 0, "size": self.size * 4}},
367
+ {"binding": 1, "resource": {"buffer": out_buf, "offset": 0, "size": self.size * 4}},
368
+ {"binding": 2, "resource": {"buffer": uniform_buf, "offset": 0, "size": 16}},
369
+ ],
370
+ )
371
+ n_workgroups = math.ceil(self.size / WORKGROUP_SIZE)
372
+ encoder = device.create_command_encoder()
373
+ pass_enc = encoder.begin_compute_pass()
374
+ pass_enc.set_pipeline(pipeline)
375
+ pass_enc.set_bind_group(0, bind_group)
376
+ pass_enc.dispatch_workgroups(n_workgroups)
377
+ pass_enc.end()
378
+ device.queue.submit([encoder.finish()])
379
+ uniform_buf.destroy()
380
+ return GPUArray(out_buf, self._shape)
381
+
382
+ # ----- argmax / argmin -----
383
+
384
+ def argmax(self) -> int:
385
+ """Return index of maximum value (as Python int)."""
386
+ data = self.to_numpy().flatten()
387
+ return int(np.argmax(data))
388
+
389
+ def argmin(self) -> int:
390
+ """Return index of minimum value (as Python int)."""
391
+ data = self.to_numpy().flatten()
392
+ return int(np.argmin(data))
393
+
394
+ # ----- copy -----
395
+
396
+ def copy(self) -> "GPUArray":
397
+ """Return a copy of this array."""
398
+ return self._run_unary_op(shaders.COPY_SHADER)
399
+
400
+ # ----- comparison operators -----
401
+
402
+ def __eq__(self, other):
403
+ if isinstance(other, (int, float)):
404
+ other = _from_numpy(np.full(self.size, other, dtype=np.float32).reshape(self._shape))
405
+ return self._run_binary_op(other, shaders.EQ_SHADER)
406
+
407
+ def __gt__(self, other):
408
+ if isinstance(other, (int, float)):
409
+ other = _from_numpy(np.full(self.size, other, dtype=np.float32).reshape(self._shape))
410
+ return self._run_binary_op(other, shaders.GT_SHADER)
411
+
412
+ def __lt__(self, other):
413
+ if isinstance(other, (int, float)):
414
+ other = _from_numpy(np.full(self.size, other, dtype=np.float32).reshape(self._shape))
415
+ return self._run_binary_op(other, shaders.LT_SHADER)
416
+
417
+ def __ge__(self, other):
418
+ if isinstance(other, (int, float)):
419
+ other = _from_numpy(np.full(self.size, other, dtype=np.float32).reshape(self._shape))
420
+ return self._run_binary_op(other, shaders.GE_SHADER)
421
+
422
+ def __le__(self, other):
423
+ if isinstance(other, (int, float)):
424
+ other = _from_numpy(np.full(self.size, other, dtype=np.float32).reshape(self._shape))
425
+ return self._run_binary_op(other, shaders.LE_SHADER)
426
+
427
+ # ----- power -----
428
+
429
+ def __pow__(self, other):
430
+ if isinstance(other, (int, float)):
431
+ other = _from_numpy(np.full(self.size, other, dtype=np.float32).reshape(self._shape))
432
+ return self._run_binary_op(other, shaders.POW_SHADER)
433
+
434
+ # ----- flatten -----
435
+
436
+ def flatten(self) -> "GPUArray":
437
+ """Return a 1D view of the array."""
438
+ return GPUArray(self._buffer, (self.size,))
439
+
440
+ # ----- len -----
441
+
442
+ def __len__(self) -> int:
443
+ return self._shape[0]
444
+
346
445
 
347
446
  def _from_numpy(data: np.ndarray) -> GPUArray:
348
447
  """Create a GPUArray from a numpy array."""
gpuarray/shaders.py CHANGED
@@ -424,3 +424,177 @@ fn main(@builtin(global_invocation_id) gid: vec3<u32>) {
424
424
  c[row * dims.N + col] = sum;
425
425
  }
426
426
  """
427
+
428
+ # ---------------------------------------------------------------------------
429
+ # Clamp (requires uniform buffer with min_val, max_val)
430
+ # ---------------------------------------------------------------------------
431
+
432
+ CLAMP_SHADER = """
433
+ struct Params {
434
+ min_val: f32,
435
+ max_val: f32,
436
+ }
437
+
438
+ @group(0) @binding(0) var<storage, read> a: array<f32>;
439
+ @group(0) @binding(1) var<storage, read_write> result: array<f32>;
440
+ @group(0) @binding(2) var<uniform> params: Params;
441
+
442
+ @compute @workgroup_size(256)
443
+ fn main(@builtin(global_invocation_id) gid: vec3<u32>) {
444
+ let i = gid.x;
445
+ if (i < arrayLength(&a)) {
446
+ result[i] = clamp(a[i], params.min_val, params.max_val);
447
+ }
448
+ }
449
+ """
450
+
451
+ # ---------------------------------------------------------------------------
452
+ # Where / select: result = cond * a + (1-cond) * b
453
+ # ---------------------------------------------------------------------------
454
+
455
+ WHERE_SHADER = """
456
+ @group(0) @binding(0) var<storage, read> cond: array<f32>;
457
+ @group(0) @binding(1) var<storage, read> a: array<f32>;
458
+ @group(0) @binding(2) var<storage, read> b: array<f32>;
459
+ @group(0) @binding(3) var<storage, read_write> result: array<f32>;
460
+
461
+ @compute @workgroup_size(256)
462
+ fn main(@builtin(global_invocation_id) gid: vec3<u32>) {
463
+ let i = gid.x;
464
+ if (i < arrayLength(&cond)) {
465
+ result[i] = cond[i] * a[i] + (1.0 - cond[i]) * b[i];
466
+ }
467
+ }
468
+ """
469
+
470
+ # ---------------------------------------------------------------------------
471
+ # Comparison shaders (return 0.0 or 1.0)
472
+ # ---------------------------------------------------------------------------
473
+
474
+ EQ_SHADER = """
475
+ @group(0) @binding(0) var<storage, read> a: array<f32>;
476
+ @group(0) @binding(1) var<storage, read> b: array<f32>;
477
+ @group(0) @binding(2) var<storage, read_write> result: array<f32>;
478
+
479
+ @compute @workgroup_size(256)
480
+ fn main(@builtin(global_invocation_id) gid: vec3<u32>) {
481
+ let i = gid.x;
482
+ if (i < arrayLength(&a)) {
483
+ if (a[i] == b[i]) {
484
+ result[i] = 1.0;
485
+ } else {
486
+ result[i] = 0.0;
487
+ }
488
+ }
489
+ }
490
+ """
491
+
492
+ GT_SHADER = """
493
+ @group(0) @binding(0) var<storage, read> a: array<f32>;
494
+ @group(0) @binding(1) var<storage, read> b: array<f32>;
495
+ @group(0) @binding(2) var<storage, read_write> result: array<f32>;
496
+
497
+ @compute @workgroup_size(256)
498
+ fn main(@builtin(global_invocation_id) gid: vec3<u32>) {
499
+ let i = gid.x;
500
+ if (i < arrayLength(&a)) {
501
+ if (a[i] > b[i]) {
502
+ result[i] = 1.0;
503
+ } else {
504
+ result[i] = 0.0;
505
+ }
506
+ }
507
+ }
508
+ """
509
+
510
+ LT_SHADER = """
511
+ @group(0) @binding(0) var<storage, read> a: array<f32>;
512
+ @group(0) @binding(1) var<storage, read> b: array<f32>;
513
+ @group(0) @binding(2) var<storage, read_write> result: array<f32>;
514
+
515
+ @compute @workgroup_size(256)
516
+ fn main(@builtin(global_invocation_id) gid: vec3<u32>) {
517
+ let i = gid.x;
518
+ if (i < arrayLength(&a)) {
519
+ if (a[i] < b[i]) {
520
+ result[i] = 1.0;
521
+ } else {
522
+ result[i] = 0.0;
523
+ }
524
+ }
525
+ }
526
+ """
527
+
528
+ GE_SHADER = """
529
+ @group(0) @binding(0) var<storage, read> a: array<f32>;
530
+ @group(0) @binding(1) var<storage, read> b: array<f32>;
531
+ @group(0) @binding(2) var<storage, read_write> result: array<f32>;
532
+
533
+ @compute @workgroup_size(256)
534
+ fn main(@builtin(global_invocation_id) gid: vec3<u32>) {
535
+ let i = gid.x;
536
+ if (i < arrayLength(&a)) {
537
+ if (a[i] >= b[i]) {
538
+ result[i] = 1.0;
539
+ } else {
540
+ result[i] = 0.0;
541
+ }
542
+ }
543
+ }
544
+ """
545
+
546
+ LE_SHADER = """
547
+ @group(0) @binding(0) var<storage, read> a: array<f32>;
548
+ @group(0) @binding(1) var<storage, read> b: array<f32>;
549
+ @group(0) @binding(2) var<storage, read_write> result: array<f32>;
550
+
551
+ @compute @workgroup_size(256)
552
+ fn main(@builtin(global_invocation_id) gid: vec3<u32>) {
553
+ let i = gid.x;
554
+ if (i < arrayLength(&a)) {
555
+ if (a[i] <= b[i]) {
556
+ result[i] = 1.0;
557
+ } else {
558
+ result[i] = 0.0;
559
+ }
560
+ }
561
+ }
562
+ """
563
+
564
+ # ---------------------------------------------------------------------------
565
+ # Fill (fill array with uniform scalar)
566
+ # ---------------------------------------------------------------------------
567
+
568
+ FILL_SHADER = """
569
+ struct Params {
570
+ scalar: f32,
571
+ }
572
+
573
+ @group(0) @binding(0) var<storage, read_write> result: array<f32>;
574
+ @group(0) @binding(1) var<uniform> params: Params;
575
+
576
+ @compute @workgroup_size(256)
577
+ fn main(@builtin(global_invocation_id) gid: vec3<u32>) {
578
+ let i = gid.x;
579
+ if (i < arrayLength(&result)) {
580
+ result[i] = params.scalar;
581
+ }
582
+ }
583
+ """
584
+
585
+ # ---------------------------------------------------------------------------
586
+ # Copy
587
+ # ---------------------------------------------------------------------------
588
+
589
+ COPY_SHADER = """
590
+ @group(0) @binding(0) var<storage, read> a: array<f32>;
591
+ @group(0) @binding(1) var<storage, read_write> result: array<f32>;
592
+
593
+ @compute @workgroup_size(256)
594
+ fn main(@builtin(global_invocation_id) gid: vec3<u32>) {
595
+ let i = gid.x;
596
+ if (i < arrayLength(&a)) {
597
+ result[i] = a[i];
598
+ }
599
+ }
600
+ """
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: gpuarray
3
- Version: 0.1.0
3
+ Version: 0.2.0
4
4
  Summary: NumPy-like GPU array library that works on every GPU — NVIDIA, AMD, Intel, Apple Silicon. No CUDA required.
5
5
  Author: Arnav Kewalram
6
6
  License: MIT
@@ -0,0 +1,9 @@
1
+ gpuarray/__init__.py,sha256=MB9k9oXPvA6wRCCZii72HtpRxjXPtkiT2RaJsiVY-wY,752
2
+ gpuarray/api.py,sha256=PlYqtvY7jEV3xIneGdRoqXJjVOP3w2fnHyZgVP6fqis,4874
3
+ gpuarray/core.py,sha256=bvBlQ9YSTRUcYawpnHpWnD-zJFdeMV16mv_rNFxYBGQ,18934
4
+ gpuarray/device.py,sha256=q80VOrKhy1u5i4AMLUKhWVBwFqQjoJ4tNBm234Wpf-A,452
5
+ gpuarray/shaders.py,sha256=LhS3vEZc08Mx_OPnsv06bT9gRPThuHIxBuWO0V2C_SI,16301
6
+ gpuarray-0.2.0.dist-info/METADATA,sha256=8ZL33ZQ7eVBnm4GuvjLxNVCiVl39u2maLCpGXbuI24w,3132
7
+ gpuarray-0.2.0.dist-info/WHEEL,sha256=K260EYznzXsJYBQGqmI8VTxEdiZYNvDZwW9cBh9-_MA,91
8
+ gpuarray-0.2.0.dist-info/top_level.txt,sha256=iruYli4LmPBqhaZxTVvzT1bT0VeN6dbC0GE29lT1CbI,9
9
+ gpuarray-0.2.0.dist-info/RECORD,,
@@ -1,9 +0,0 @@
1
- gpuarray/__init__.py,sha256=yDS9YEnJUsiPgAnftBGRQJqG2FwL6oRwvOI98p7WZu8,568
2
- gpuarray/api.py,sha256=tlIjtSCmCs-CYbZSAmW5XPxAxZmmSX3-wuGXI-6u1Zs,2090
3
- gpuarray/core.py,sha256=GIs-PE6Y7m0htoBv1B1hbGxImkmLFkP6z_Zo9Gm5Joc,15122
4
- gpuarray/device.py,sha256=q80VOrKhy1u5i4AMLUKhWVBwFqQjoJ4tNBm234Wpf-A,452
5
- gpuarray/shaders.py,sha256=7fuWEtPVVHXnJxFBeT5SoQ467FDCqM6fP14Wqd8r0HA,11411
6
- gpuarray-0.1.0.dist-info/METADATA,sha256=WZHK9RoABh502yZZ7s8U4h4vy4UGknpfOQ1duSeb-3I,3132
7
- gpuarray-0.1.0.dist-info/WHEEL,sha256=K260EYznzXsJYBQGqmI8VTxEdiZYNvDZwW9cBh9-_MA,91
8
- gpuarray-0.1.0.dist-info/top_level.txt,sha256=iruYli4LmPBqhaZxTVvzT1bT0VeN6dbC0GE29lT1CbI,9
9
- gpuarray-0.1.0.dist-info/RECORD,,