cumo 0.13.0 → 0.14.0
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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +17 -0
- data/README.md +50 -0
- data/cumo.gemspec +10 -1
- data/ext/cumo/cuda/cufft.c +605 -0
- data/ext/cumo/cuda/driver.c +1 -0
- data/ext/cumo/cuda/memory_pool.cpp +2 -0
- data/ext/cumo/cumo.c +2 -2
- data/ext/cumo/extconf.rb +8 -0
- data/ext/cumo/include/cumo/cuda/cufft.h +38 -0
- data/ext/cumo/include/cumo.h +0 -3
- data/ext/cumo/narray/narray.c +0 -2
- data/lib/cumo/fft.rb +482 -0
- data/lib/cumo/linalg.rb +19 -8
- data/lib/cumo/narray/extra.rb +31 -0
- data/lib/cumo/pocketfft.rb +134 -0
- data/lib/cumo/version.rb +8 -0
- data/lib/cumo.rb +1 -0
- metadata +7 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: f09e6c09816ea16f1a27425e44170c7ea191b771f0a3b26b2b7298a1e53b6e6c
|
|
4
|
+
data.tar.gz: f4739b41e92d5f5920ea694393f3ab71241e9b428a29416db71e91d5fcb0f13e
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: d58332736efabf28caeec996882ceb99435885e41d017a40911c6a85346a4f03428e32cb7cae32e8dae9633e47f7497dc35075309ec32d61d607504ef5f61fb1
|
|
7
|
+
data.tar.gz: d276cc4b4d19203ab34bfae56e96dd6e3220a780f07d3da8934e9c9241336fcb31c2040e013a7e461b47ec93461dcff21ca126597742c184e52221c0112bf196
|
data/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,20 @@
|
|
|
1
|
+
# 0.14.0 (2026/10/18)
|
|
2
|
+
|
|
3
|
+
Breaking changes:
|
|
4
|
+
|
|
5
|
+
* `cumo.h` no longer defines `CUMO_VERSION` or `CUMO_VERSION_CODE`. `Cumo::VERSION` and `Cumo::NArray::VERSION` are defined in Ruby, and the new `Cumo::REVISION` tells a release gem from one built from a checkout: it gives `"v0.14.0"` for this release and something like `"v0.14.0-3-g1a2b3c4"` for a gem built at a later commit (PR #617)
|
|
6
|
+
|
|
7
|
+
Fixes:
|
|
8
|
+
|
|
9
|
+
* Fix a host loop such as `each_with_index` yielding the old values of an array after a kernel launched from its block through `Function#launch`, `ElementwiseKernel` or `ReductionKernel` wrote the array (PR #625)
|
|
10
|
+
|
|
11
|
+
Changes:
|
|
12
|
+
|
|
13
|
+
* Add `Cumo::FFT`, the functions of `numpy.fft` on cuFFT with numpy's arguments as keywords: `fft`, `ifft`, `rfft`, `irfft`, `hfft`, `ihfft`, `fft2`, `ifft2`, `rfft2`, `irfft2`, `fftn`, `ifftn`, `rfftn`, `irfftn`, `fftfreq`, `rfftfreq`, `fftshift` and `ifftshift`, and `fftconvolve`, which answers the full linear convolution as `scipy.signal.fftconvolve` does. Load it with `require "cumo/fft"`. Single precision stays single, as in CuPy. cuFFT is loaded the first time a transform runs, and `Cumo::CUDA::Cufft.available?` says whether it can be. A thread keeps up to 16 plans for each device, which `Cumo::CUDA::Cufft.plan_cache_info` and `clear_plan_cache` show and let go (PR #623, PR #622, PR #621, PR #620)
|
|
14
|
+
* Add `Cumo::Pocketfft`, the functions of `Numo::Pocketfft` from numo-pocketfft on `Cumo::FFT`, with the same arguments, return types and errors. Load it with `require "cumo/pocketfft"`. A 0-dimensional array raises `Cumo::NArray::DimensionError`, where numo-pocketfft fails on it in a different way in each function (PR #623)
|
|
15
|
+
* Add `Cumo::NArray.from_numo` and `Cumo::NArray.to_numo`, which copy an array between Numo and Cumo and keep its type, where `Cumo::NArray.cast` read the elements one by one through a Ruby Array and turned an SFloat into a DFloat (closes issue #87, PR #619)
|
|
16
|
+
* `Cumo::Linalg.expm` of a dense matrix that needs squaring skips the check for a triangular matrix that 0.13.0 added, which makes a 64 x 64 expm about 8% faster in SFloat and 6% in DFloat (PR #618)
|
|
17
|
+
|
|
1
18
|
# 0.13.0 (2026/10/12)
|
|
2
19
|
|
|
3
20
|
Breaking changes:
|
data/README.md
CHANGED
|
@@ -97,6 +97,24 @@ end
|
|
|
97
97
|
a = xm::DFloat.new(3,5).seq
|
|
98
98
|
```
|
|
99
99
|
|
|
100
|
+
### Moving Arrays Between Numo And Cumo
|
|
101
|
+
|
|
102
|
+
`Cumo::NArray.from_numo` copies a Numo array to the GPU, and `Cumo::NArray.to_numo` copies a Cumo array back to the host.
|
|
103
|
+
The type keeps its name, so a `Numo::Int32` becomes a `Cumo::Int32` and back.
|
|
104
|
+
Both copy the bytes, where `Cumo::NArray.cast` would read the elements one by one through a Ruby Array.
|
|
105
|
+
A byte-swapped array arrives in host order.
|
|
106
|
+
|
|
107
|
+
```ruby
|
|
108
|
+
require 'numo/narray'
|
|
109
|
+
require 'cumo/narray'
|
|
110
|
+
|
|
111
|
+
a = Cumo::NArray.from_numo(Numo::SFloat.new(3,5).seq) #=> Cumo::SFloat
|
|
112
|
+
b = Cumo::NArray.to_numo(a * 2) #=> Numo::SFloat
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
Cumo does not depend on Numo. `to_numo` requires `numo/narray` when it is called, so Numo has to be installed for it.
|
|
116
|
+
`HFloat`, `BFloat` and `RObject` raise `TypeError`, since Numo has no half-precision types and an `RObject` holds Ruby objects rather than bytes.
|
|
117
|
+
|
|
100
118
|
### Incompatibility With Numo
|
|
101
119
|
|
|
102
120
|
Numo returns a Ruby numeric object wherever a result is 0-dimensional, while Cumo returns the 0-dimensional NArray itself.
|
|
@@ -318,6 +336,38 @@ A function waits for the current stream when it answers a Ruby number, or when i
|
|
|
318
336
|
`solve`, `inv` and `det` read back whether the matrix was singular, `cho_fact` whether it was positive definite, and `eigh` and `svd` whether they converged.
|
|
319
337
|
`matmul`, `dot` of two matrices, `matrix_power` to a positive power, `cholesky`, `cho_solve`, `qr` and `norm` along an axis wait for nothing, so a run of them is queued without the host waiting.
|
|
320
338
|
|
|
339
|
+
### Fourier Transforms
|
|
340
|
+
|
|
341
|
+
`require "cumo/fft"` brings in `Cumo::FFT`, the functions of `numpy.fft` on the GPU through cuFFT.
|
|
342
|
+
They take numpy's arguments as keywords: `n:`, `axis:` and `norm:` for the one-dimensional transforms, and `s:`, `axes:` and `norm:` for the others.
|
|
343
|
+
|
|
344
|
+
```ruby
|
|
345
|
+
require "cumo/fft"
|
|
346
|
+
|
|
347
|
+
x = Cumo::SFloat.new(1024).rand
|
|
348
|
+
spectrum = Cumo::FFT.rfft(x)
|
|
349
|
+
back = Cumo::FFT.irfft(spectrum, n: 1024)
|
|
350
|
+
image = Cumo::FFT.fft2(Cumo::DComplex.new(256, 256).rand, norm: "ortho")
|
|
351
|
+
freqs = Cumo::FFT.fftfreq(1024, d: 0.01)
|
|
352
|
+
```
|
|
353
|
+
|
|
354
|
+
The functions are `fft`, `ifft`, `rfft`, `irfft`, `hfft`, `ihfft`, `fft2`, `ifft2`, `rfft2`, `irfft2`, `fftn`, `ifftn`, `rfftn`, `irfftn`, `fftfreq`, `rfftfreq`, `fftshift` and `ifftshift`, as in numpy.
|
|
355
|
+
`fftconvolve(a, b)` answers the full linear convolution, as `scipy.signal.fftconvolve` does in its `"full"` mode, and pads each axis to a length cuFFT computes fast on the way.
|
|
356
|
+
Single precision stays single: `SFloat`, `SComplex`, `HFloat` and `BFloat` give `SComplex`, or `SFloat` for a real result, and every other type gives `DComplex` or `DFloat`, as in numpy 2 and CuPy.
|
|
357
|
+
Errors follow numpy: an axis out of range raises `Cumo::NArray::DimensionError`, a transform of no points `ArgumentError`, and a complex input to `rfft` `TypeError`.
|
|
358
|
+
|
|
359
|
+
Cumo looks for the cuFFT header when it is built, and loads the library the first time a transform runs, so `require "cumo"` does not pay for it.
|
|
360
|
+
`Cumo::CUDA::Cufft.available?` answers whether it can be loaded.
|
|
361
|
+
`fftfreq`, `rfftfreq`, `fftshift` and `ifftshift` work without it.
|
|
362
|
+
|
|
363
|
+
A thread keeps up to 16 cuFFT plans for each device, and reuses one when a transform of the same type and shape comes again.
|
|
364
|
+
`Cumo::CUDA::Cufft.plan_cache_info` answers how many plans it holds and how often they were reused, and `Cumo::CUDA::Cufft.clear_plan_cache` lets them go.
|
|
365
|
+
A thread's plans also go when it ends, and when an allocation it makes from the memory pool runs out of memory.
|
|
366
|
+
|
|
367
|
+
`require "cumo/pocketfft"` brings in `Cumo::Pocketfft`, the functions of `Numo::Pocketfft` from [numo-pocketfft](https://github.com/yoshoku/numo-pocketfft).
|
|
368
|
+
They keep its single positional argument, its double precision results and the `ArgumentError`s it raises, so code written for numo-pocketfft runs on the GPU after the replacement of `Numo` with `Cumo` above.
|
|
369
|
+
A 0-dimensional array raises `Cumo::NArray::DimensionError`, where numo-pocketfft fails on it in a different way in each function.
|
|
370
|
+
|
|
321
371
|
### Fused Operations
|
|
322
372
|
|
|
323
373
|
`layer_norm`, `rms_norm` and `softmax` normalize along the last axis in one kernel each, and `quantize_symmetric` takes it to 8-bit integers in one more.
|
data/cumo.gemspec
CHANGED
|
@@ -3,7 +3,8 @@
|
|
|
3
3
|
lib = File.expand_path("../lib", __FILE__)
|
|
4
4
|
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
|
5
5
|
|
|
6
|
-
|
|
6
|
+
# Requiring version.rb here would fix Cumo::REVISION to nil under bundle exec.
|
|
7
|
+
cumo_version = File.read(File.join(__dir__, "lib/cumo/version.rb"))[/VERSION = "([^"]+)"/, 1]
|
|
7
8
|
|
|
8
9
|
Gem::Specification.new do |spec|
|
|
9
10
|
spec.name = "cumo"
|
|
@@ -28,6 +29,14 @@ Gem::Specification.new do |spec|
|
|
|
28
29
|
spec.files = `git ls-files -z`.split("\x0").reject do |f|
|
|
29
30
|
f.start_with?(*%w[.github/ test/ examples/ bin/ bench/ docker- Dockerfile Gemfile .gitignore .rubocop])
|
|
30
31
|
end
|
|
32
|
+
|
|
33
|
+
revision = begin
|
|
34
|
+
IO.popen(%W[git -C #{__dir__} describe --tags --match v[0-9]* --dirty --always], err: File::NULL, &:read).strip
|
|
35
|
+
rescue SystemCallError
|
|
36
|
+
""
|
|
37
|
+
end
|
|
38
|
+
spec.metadata["cumo_revision"] = revision unless revision.empty?
|
|
39
|
+
|
|
31
40
|
spec.bindir = "exe"
|
|
32
41
|
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
|
33
42
|
spec.require_paths = ["lib"]
|