convolver 4.0.0 → 4.0.1

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 0461fd5af32c0104f8214df035709fea95c2b50f21f63d1b88c7999cadcafd29
4
- data.tar.gz: 770518e0498a79c213c3be551f7749d6e20139a9fd327357f1d3608c0cbb72fd
3
+ metadata.gz: d27be8d916e59d307ddaef4f74bdb2d6be83fc10e8b12ee3931d5136e9f7457e
4
+ data.tar.gz: db067d222f36caa4311a2e557aad198a16514092264e2929f6ff7e4fd9005334
5
5
  SHA512:
6
- metadata.gz: 40b871a7ad8883742b27ff4b05ae830f38cbdd7f09f3fc6ef85fd26166bbc9613fe3fc7b3d73bd47f6ce45642dfa3e4b884bc69f9b88bc69028a043d5da5e4de
7
- data.tar.gz: 506e52bd6f023d6fb169589e1763fe2a3668baf5158a04e545dd8bbc4de151e4b03ece49a51dd9b6d1b0f71666a662d9a148a269ecd96cd73c9ce6d0a2c0fa2b
6
+ metadata.gz: 8a3f955a008e8af26f3cae3c00b25854c10497f55dac328db8b063fa4190360c6b7a0ea87e80e4ba1a5511e525108615a0ff2202b412b9b9f700cd992fa5ba1a
7
+ data.tar.gz: 810fc3b67314bb846ec8363c3fe9ae205e01eef5c10b003eaafa72a3623a27169d1667db07ec0badca20aca19ae0b233e2a0b7a7a2b4fd3a1cc0dccd29655e2c
data/CHANGELOG.md CHANGED
@@ -5,6 +5,28 @@ All notable changes to this project will be documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## [4.0.1] - 2026-10-06
9
+
10
+ ### Added
11
+
12
+ - Packaged Markdown references for calculation rules and terminology.
13
+ - `rake docs:build` to generate linked API and reference pages; the existing
14
+ documentation gate also builds the pages and checks local links and anchors.
15
+
16
+ ### Changed
17
+
18
+ - Explain public methods through purpose and worked examples, with shared
19
+ option rules and clear precision, alignment and memory-limit descriptions.
20
+ - Shorten README around common usage and preserve detailed rules in the reference.
21
+ Calculation behavior, supported inputs and method signatures are unchanged.
22
+
23
+ ### Fixed
24
+
25
+ - Preserve the public module overview in generated documentation, clarify valid
26
+ convolution indexing, and correct inherited method/error descriptions.
27
+ - Repair changelog comparison links, using the original source commit for the
28
+ untagged 3.0.0 version.
29
+
8
30
  ## [4.0.0] - 2026-10-06
9
31
 
10
32
  ### Added
@@ -162,8 +184,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
162
184
  - Updated the native extension to use the maintained Numo C API and removed the
163
185
  legacy untyped-data compatibility code.
164
186
 
165
- [Unreleased]: https://github.com/neilslater/convolver/compare/v3.0.1...HEAD
166
- [3.0.0]: https://github.com/neilslater/convolver/compare/v2.0.0...v3.0.0
187
+ [4.0.1]: https://github.com/neilslater/convolver/compare/v4.0.0...v4.0.1
188
+ [4.0.0]: https://github.com/neilslater/convolver/compare/v3.0.1...v4.0.0
189
+ [3.0.1]: https://github.com/neilslater/convolver/compare/9c12a003b3b8675c9cbde0556c586da6635d872e...v3.0.1
190
+ [3.0.0]: https://github.com/neilslater/convolver/compare/v2.0.0...9c12a003b3b8675c9cbde0556c586da6635d872e
167
191
  [2.0.0]: https://github.com/neilslater/convolver/compare/v1.0.1...v2.0.0
168
192
  [1.0.1]: https://github.com/neilslater/convolver/compare/v1.0.0...v1.0.1
169
193
  [1.0.0]: https://github.com/neilslater/convolver/compare/v0.3.2...v1.0.0
data/README.md CHANGED
@@ -3,13 +3,12 @@
3
3
  [![CI](https://github.com/neilslater/convolver/actions/workflows/ci.yml/badge.svg?branch=main)](https://github.com/neilslater/convolver/actions/workflows/ci.yml)
4
4
  [![Gem Version](https://badge.fury.io/rb/convolver.svg)](https://badge.fury.io/rb/convolver)
5
5
 
6
- Convolver calculates mathematical convolution and cross-correlation between
7
- multidimensional
8
- [`Numo::NArray`](https://github.com/yoshoku/numo-narray-alt) values. Both
9
- operations support configurable output extents and signal boundary extensions.
10
- Convolver chooses between a direct native implementation for smaller inputs
11
- and a [`Numo::Pocketfft`](https://github.com/yoshoku/numo-pocketfft)-based
12
- implementation for larger inputs.
6
+ Convolver combines numeric sequences or grids with a kernel of weights. Use it
7
+ for operations such as smoothing readings, applying an image filter or matching
8
+ a pattern. Inputs are [`Numo::NArray`](https://github.com/yoshoku/numo-narray-alt)
9
+ arrays. Convolver chooses between direct calculation and
10
+ [`PocketFFT`](https://github.com/yoshoku/numo-pocketfft) transforms using estimates
11
+ of their cost.
13
12
 
14
13
  ## Installation
15
14
 
@@ -19,188 +18,97 @@ Add Convolver to your application's Gemfile:
19
18
  gem 'convolver'
20
19
  ```
21
20
 
22
- Then run `bundle install`, or install the gem directly with
23
- `gem install convolver`. No external FFT library is required; PocketFFT is
24
- bundled by its Ruby gem.
21
+ Then run `bundle install`, or install it directly with `gem install convolver`.
22
+ Ruby 3.3 or newer and a toolchain able to build native extensions are required.
23
+ No external FFT library is needed; PocketFFT is bundled by its Ruby gem.
25
24
 
26
- ## Usage
25
+ ## Start with a small example
27
26
 
28
- `convolve` calculates mathematical discrete convolution. `correlate`
29
- calculates cross-correlation, following the public naming convention used by
30
- NumPy and SciPy:
27
+ A kernel assigns a weight to each value in a moving window. Convolution reverses
28
+ those weights within the window; cross-correlation keeps their stored order.
29
+ Pass the original kernel to either method.
31
30
 
32
31
  ```ruby
33
32
  require 'convolver'
34
33
 
35
- signal = Numo::SFloat[0.3, 0.4, 0.5]
36
- kernel = Numo::SFloat[1.3, -0.5]
34
+ signal = Numo::DFloat[1, 2, 4, 8, 16]
35
+ kernel = Numo::DFloat[1, 2, 3]
37
36
 
38
- Convolver.convolve(signal, kernel)
39
- # => Numo::SFloat#shape=[2]
40
- # [0.37, 0.45]
41
-
42
- Convolver.correlate(signal, kernel)
43
- # => Numo::SFloat#shape=[2]
44
- # [0.19, 0.27]
45
- ```
46
-
47
- For real one-dimensional inputs, the two valid operations are:
48
-
49
- ```text
50
- correlate(signal, kernel)[p] = sum_j signal[p + j] * conjugate(kernel[j])
51
- convolve(signal, kernel)[n] = sum_j signal[n - j] * kernel[j]
37
+ Convolver.convolve(signal, kernel).to_a # => [11.0, 22.0, 44.0]
38
+ Convolver.correlate(signal, kernel).to_a # => [17.0, 34.0, 68.0]
52
39
  ```
53
40
 
54
- The formulas apply component-wise to multidimensional arrays. Correlation
55
- conventionally conjugates the second operand, although Convolver's current
56
- real input contract makes conjugation invisible.
41
+ The first convolution result is `1*3 + 2*2 + 4*1 = 11`. The corresponding
42
+ correlation result is `1*1 + 2*2 + 4*3 = 17`. Neither method modifies its inputs.
57
43
 
58
- With no keywords, the signal and kernel must have the same rank, the kernel
59
- must be no larger than the signal in any dimension, and only positions with
60
- complete overlap are returned. Both inputs determine the floating result dtype,
61
- or you can select it explicitly with `dtype:`.
44
+ Use `convolve` for mathematical convolution, such as applying a smoothing kernel
45
+ or combining independent probability distributions. Use `correlate` to compare
46
+ windows with a pattern. Correlation here is a sum of products, not a normalized
47
+ correlation coefficient.
62
48
 
63
- The automatic, direct, and FFT implementations are available for both
64
- operations:
49
+ ## Keep the input size or include the edges
65
50
 
66
- ```ruby
67
- Convolver.convolve(signal, kernel)
68
- Convolver.convolve_basic(signal, kernel)
69
- Convolver.convolve_fft(signal, kernel)
51
+ By default, Convolver returns only complete windows. `mode:` changes the output
52
+ size along each dimension, where `S` is the signal size and `K` the kernel size:
70
53
 
71
- Convolver.correlate(signal, kernel)
72
- Convolver.correlate_basic(signal, kernel)
73
- Convolver.correlate_fft(signal, kernel)
74
- ```
54
+ | Mode | Positions included | Result size |
55
+ | --- | --- | --- |
56
+ | `:valid` (default) | Complete windows inside the signal | `S - K + 1` |
57
+ | `:same` | One output for each signal position | `S` |
58
+ | `:full` | All positions with any signal overlap | `S + K - 1` |
75
59
 
76
- Every calculation method accepts the same options:
60
+ For example, a centered moving average can keep the input size and reflect
61
+ values at the edges:
77
62
 
78
63
  ```ruby
79
- Convolver.convolve(signal, kernel,
80
- mode: :same,
81
- boundary: :reflect,
82
- origin: 0)
64
+ signal = Numo::DFloat[1, 2, 4, 8, 16]
65
+ kernel = Numo::DFloat.ones(3) / 3
66
+ smoothed = Convolver.convolve(signal, kernel, mode: :same, boundary: :reflect)
67
+ smoothed.shape # => [5]
68
+ smoothed.to_a.map { |value| value.round(3) } # => [1.333, 2.333, 4.667, 9.333, 13.333]
83
69
  ```
84
70
 
85
- ### Input and result precision
71
+ Boundary choices are constant fill, nearest value, reflection with or without
72
+ repeating the endpoint, and periodic wrap. `:same` supports all of them;
73
+ `:full` supports constant fill only. For nonconstant boundaries, omit
74
+ `fill_value:` entirely, even when its value would be zero.
86
75
 
87
- `dtype:` accepts `Numo::SFloat`, `Numo::DFloat`, or `nil` (automatic, the
88
- default). Automatic selection maps each input type using this table, then
89
- chooses DFloat if either input maps to DFloat, otherwise SFloat:
76
+ The [calculation rules](docs/rules.md) show each boundary, allowed option
77
+ combinations, and how `origin:` aligns odd- and even-length kernels.
90
78
 
91
- | Input type | Automatic floating type |
92
- | --- | --- |
93
- | `SFloat`, `Int8`, `UInt8`, `Int16`, `UInt16` | `Numo::SFloat` |
94
- | `DFloat`, `Int32`, `UInt32`, `Int64`, `UInt64` | `Numo::DFloat` |
79
+ ## Choose precision and calculation path
95
80
 
96
- The input type names above are classes in `Numo`. Both operands must be instances
97
- of these concrete classes. Bit, RObject, complex arrays, custom subclasses and
98
- ordinary Ruby arrays are rejected, even with an explicit `dtype:`. Integer
99
- results and complex arithmetic are not supported.
81
+ The result is `Numo::SFloat` or `Numo::DFloat`, chosen from both inputs.
82
+ `dtype: nil` means automatic selection; either floating class can be requested
83
+ explicitly:
100
84
 
101
85
  ```ruby
102
- signal = Numo::SFloat[1, 2, 3]
103
- kernel = Numo::DFloat[0.25, 0.75]
104
- Convolver.convolve(signal, kernel) # DFloat result
105
- Convolver.convolve(signal, kernel, dtype: Numo::SFloat) # SFloat result
86
+ signal = Numo::DFloat[1, 2, 4]
87
+ kernel = Numo::DFloat[1, 2]
88
+ Convolver.convolve(signal, kernel, dtype: Numo::SFloat).class # => Numo::SFloat
106
89
  ```
107
90
 
108
- Both inputs are cast to the selected dtype before extension, folding or
109
- arithmetic. Inputs are never mutated. `fill_value` is cast to this same dtype
110
- and never promotes the result. Ordinary floating conversion applies, including
111
- rounding, overflow to infinity and underflow; NaN and infinity are not rejected.
112
- Non-finite propagation and rounding can differ between algorithms.
91
+ Integer inputs are accepted according to the supported-class table in the
92
+ rules, but results and arithmetic are floating-point. Convert to a floating
93
+ array before dividing to make kernel weights. A later `dtype:` override cannot
94
+ recover information already lost to integer division or rounding.
113
95
 
114
- Direct products and accumulation use the selected precision. PocketFFT uses
115
- double-precision working buffers for either result dtype. The returned class
116
- is independent of algorithm selection. Integer inputs become approximate
117
- floating computations: DFloat represents every 32-bit integer, but not every
118
- 64-bit integer, and neither dtype guarantees exact integer convolution.
96
+ Start with `convolve` or `correlate` for automatic selection. To request a
97
+ particular path, use `convolve_basic` / `correlate_basic` for direct calculation
98
+ or `convolve_fft` / `correlate_fft` for FFT calculation. Both return the same
99
+ shape and class; rounding can differ, so compare values with suitable tolerances.
100
+ Neither automatic selection nor the `predict_*_time` estimates guarantee the
101
+ fastest method or a measured running time.
119
102
 
120
- ### Output modes
103
+ ## Reference documentation
121
104
 
122
- `mode:` controls the returned extent independently in each dimension:
123
-
124
- | Mode | Meaning | Result size |
125
- | --- | --- | --- |
126
- | `:valid` | Kernel overlaps the stored signal completely | `S - K + 1` |
127
- | `:same` | One result aligned with each stored signal position | `S` |
128
- | `:full` | Every kernel position with any stored-signal overlap | `S + K - 1` |
129
-
130
- `:valid` is the default and accepts only the default `boundary: :constant`,
131
- `fill_value: 0.0`, and `origin: 0`. `:full` supports constant extension only.
132
- `:same` supports every boundary described below. Kernels larger than the signal
133
- are supported by `:same` and `:full`, but not by `:valid`. `mode:` and
134
- `boundary:` accept only the symbols listed here.
135
-
136
- ### Boundary extension
137
-
138
- For a one-dimensional signal `a b c d`, `boundary:` selects values outside the
139
- stored signal:
140
-
141
- | Boundary | Extended sequence |
142
- | --- | --- |
143
- | `:constant` | `k k k k | a b c d | k k k k` |
144
- | `:nearest` | `a a a a | a b c d | d d d d` |
145
- | `:reflect` | `d c b a | a b c d | d c b a` |
146
- | `:mirror` | `d c b | a b c d | c b a` |
147
- | `:wrap` | `a b c d | a b c d | a b c d` |
148
-
149
- `fill_value:` sets `k` for `:constant` and defaults to zero. It must not be
150
- passed with another boundary. `:reflect` repeats the edge sample; `:mirror`
151
- does not. All boundary modes work across every dimension, including
152
- length-one axes and extensions wider than the stored signal.
153
-
154
- ### Kernel origin and alignment
155
-
156
- `origin:` shifts the stored-kernel anchor for `:same`. It accepts one integer
157
- applied to every dimension or an array with one integer per dimension. For a
158
- kernel dimension of length `K`:
159
-
160
- ```text
161
- anchor = floor(K / 2) + origin
162
-
163
- correlation[i] = sum_j extended_signal[i + j - anchor] * kernel[j]
164
- convolution[i] = sum_j extended_signal[i + anchor - j] * kernel[j]
165
- ```
166
-
167
- The anchor must remain within the kernel. Correlation pads `anchor` samples
168
- before the signal and `K - 1 - anchor` after it; convolution swaps those
169
- widths. Odd centered kernels therefore align alike. For an even centered
170
- kernel, correlation puts the extra sample before the signal and convolution
171
- puts it after the signal. Positive origins move correlation toward lower signal
172
- indices and convolution toward higher signal indices.
173
-
174
- Nonzero origins are supported only for `:same`; `:valid` and `:full` require
175
- zero.
176
-
177
- ### Estimators
178
-
179
- The estimator methods accept and validate the same options as their calculation
180
- families:
181
-
182
- ```ruby
183
- Convolver.predict_convolve_basic_time(signal, kernel, mode: :same, boundary: :nearest)
184
- Convolver.predict_convolve_fft_time(signal, kernel, mode: :same, boundary: :wrap)
185
-
186
- Convolver.predict_correlate_basic_time(signal, kernel, mode: :same, boundary: :nearest)
187
- Convolver.predict_correlate_fft_time(signal, kernel, mode: :same, boundary: :wrap)
188
- ```
105
+ - [Input and calculation rules](docs/rules.md): exact supported classes,
106
+ precision, output formulas, boundaries, alignment, estimates and memory limits.
107
+ - [Terminology](docs/terminology.md): explanations of the words used in the API.
189
108
 
190
- Estimates are machine-dependent heuristics, including input conversion, boundary
191
- preparation and periodic kernel folding. Automatic selection avoids FFT planning
192
- when a cheap lower bound already favors direct calculation. Ranks one through
193
- three use calibrated profiles on arm64 macOS and x86_64/aarch64 Linux; other
194
- platforms and higher ranks retain the general heuristic. Algorithm choices
195
- can change as estimates improve; benchmark the explicit methods for workloads
196
- where the choice matters.
197
-
198
- Calculations and estimators reject unrepresentable buffer sizes with `RangeError`
199
- before allocation. FFT planning also checks conservative PocketFFT integer and
200
- work-buffer limits, including full complex staging for real inverse transforms.
201
- Automatic selection uses direct calculation when FFT exceeds its limits and the
202
- direct path remains valid. These checks do not reserve memory; ordinary allocator
203
- exhaustion still raises `NoMemoryError`.
109
+ These reference files are included in the gem. From a development checkout,
110
+ `bundle exec rake docs:build` builds the API reference and these pages together
111
+ in `doc/index.html`.
204
112
 
205
113
  ### Migrating from version 3
206
114
 
@@ -249,8 +157,9 @@ bundle exec rake docs:check
249
157
  The Ruby specs exercise both the Ruby API and native extension and enforce 95%
250
158
  line and branch coverage for the Ruby library. Dependency auditing refreshes
251
159
  the advisory database and requires network access. The documentation check
252
- rejects YARD warnings and undocumented public API objects; private implementation
253
- classes are marked `@private`. Additional native-code checks are available:
160
+ builds the linked pages in `doc/`, checks local link destinations and anchors,
161
+ and rejects YARD warnings and undocumented public API objects. Private
162
+ implementation classes are marked `@private`. Additional native-code checks are available:
254
163
 
255
164
  ```sh
256
165
  bundle exec rake c:coverage # Requires GCC and gcovr
data/docs/rules.md ADDED
@@ -0,0 +1,206 @@
1
+ # Convolver input and calculation rules
2
+
3
+ These rules describe Convolver 4. They apply to both convolution and
4
+ cross-correlation. See the [terminology](terminology.md) for unfamiliar words,
5
+ or [README](../README.md) for installation and a quick example.
6
+
7
+ ## Choose an operation
8
+
9
+ Use `Convolver.convolve` for mathematical convolution and `Convolver.correlate`
10
+ for cross-correlation. Both slide a kernel over a signal, multiply paired values
11
+ and add the products. Convolution reverses the kernel within each window;
12
+ correlation keeps its stored order. Pass the kernel in its original order in
13
+ either case: Convolver performs the named operation itself.
14
+
15
+ ```ruby
16
+ require 'convolver'
17
+
18
+ signal = Numo::DFloat[1, 2, 4, 8, 16]
19
+ kernel = Numo::DFloat[1, 2, 3]
20
+
21
+ Convolver.convolve(signal, kernel).to_a # => [11.0, 22.0, 44.0]
22
+ Convolver.correlate(signal, kernel).to_a # => [17.0, 34.0, 68.0]
23
+ ```
24
+
25
+ For the first window, convolution calculates `1*3 + 2*2 + 4*1 = 11`.
26
+ Correlation calculates `1*1 + 2*2 + 4*3 = 17`.
27
+
28
+ Each operation has three entry points:
29
+
30
+ | Choice | Convolution | Cross-correlation |
31
+ | --- | --- | --- |
32
+ | Automatic choice | `convolve` | `correlate` |
33
+ | Direct calculation | `convolve_basic` | `correlate_basic` |
34
+ | FFT calculation | `convolve_fft` | `correlate_fft` |
35
+
36
+ Start with the automatic methods. They estimate which implementation is likely
37
+ to be faster; they do not guarantee the fastest choice for every machine or
38
+ input. Explicit methods are useful for measurements or when a workload needs a
39
+ particular calculation path.
40
+
41
+ ## Supply compatible arrays
42
+
43
+ Both inputs must be nonempty Numo arrays with the same number of dimensions,
44
+ up to `Convolver::MAX_RANK` (16). Their sizes may differ. Two zero-dimensional
45
+ arrays are supported and their scalar values are multiplied.
46
+
47
+ The supported input classes and their automatic floating types are:
48
+
49
+ | Input class in `Numo` | Automatic floating type |
50
+ | --- | --- |
51
+ | `SFloat`, `Int8`, `UInt8`, `Int16`, `UInt16` | `Numo::SFloat` |
52
+ | `DFloat`, `Int32`, `UInt32`, `Int64`, `UInt64` | `Numo::DFloat` |
53
+
54
+ Inputs must be instances of these concrete classes. Ordinary Ruby arrays,
55
+ `Numo::Bit`, complex arrays, `Numo::RObject` and custom subclasses are rejected,
56
+ even when `dtype:` is supplied. Supported array views, including slices and
57
+ reversed views, are accepted. Neither input is modified.
58
+
59
+ ## Choose precision
60
+
61
+ By default, Convolver maps each input using the table above. If either maps to
62
+ `Numo::DFloat`, the result is DFloat; otherwise it is SFloat. Swapping the input
63
+ types does not change this choice. `dtype: nil` means the same as omitting it.
64
+
65
+ Use `dtype: Numo::SFloat` or `dtype: Numo::DFloat` to choose explicitly. These
66
+ classes and `nil` are the only accepted dtype values. Both inputs and any
67
+ constant fill are converted to the chosen type before preparing boundary
68
+ values or calculating. The fill value does not influence the choice of dtype.
69
+
70
+ Choose floating inputs before dividing to make kernel weights. Integer division
71
+ can discard fractions before Convolver sees them:
72
+
73
+ ```ruby
74
+ Numo::Int16.ones(3) / 3 # => integer weights [0, 0, 0]
75
+ Numo::DFloat.ones(3) / 3 # => floating weights, each approximately one third
76
+ ```
77
+
78
+ A later dtype override cannot recover information already lost to integer
79
+ division or floating-point rounding. Integer inputs produce approximate floating
80
+ calculations, not integer results. DFloat can represent every 32-bit integer,
81
+ but not every 64-bit integer. Neither result type guarantees exact integer
82
+ convolution.
83
+
84
+ The direct path multiplies and adds in the chosen precision. The FFT path uses
85
+ double-precision working buffers, then returns the chosen result type. Shapes
86
+ and result classes agree across paths, but rounding can differ. Compare numeric
87
+ results with a tolerance appropriate to the data and precision.
88
+
89
+ Normal floating conversion applies, including overflow to infinity and
90
+ underflow. NaN and infinity are accepted, but their propagation can differ
91
+ between calculation paths.
92
+
93
+ ## Choose output size
94
+
95
+ `mode:` controls the size of the result. In this table, `S` is the signal size
96
+ and `K` the kernel size along one dimension. Apply the rule to each dimension.
97
+
98
+ | Mode | Positions included | Result size |
99
+ | --- | --- | --- |
100
+ | `:valid` (default) | Only windows fully inside the stored signal | `S - K + 1` |
101
+ | `:same` | One output aligned with each stored signal position | `S` |
102
+ | `:full` | All positions with any stored-signal overlap | `S + K - 1` |
103
+
104
+ In valid mode, the kernel must fit inside the signal in every dimension. Larger
105
+ kernels are permitted in same and full modes. Equal numbers of dimensions are
106
+ still required; a one-dimensional kernel is not automatically applied to each
107
+ row of a two-dimensional signal.
108
+
109
+ For one-dimensional valid results, returned index `p` starts at zero:
110
+
111
+ ```text
112
+ convolve(signal, kernel)[p] = sum_j signal[p + K - 1 - j] * kernel[j]
113
+ correlate(signal, kernel)[p] = sum_j signal[p + j] * kernel[j]
114
+ ```
115
+
116
+ Here `j` runs from zero to `K - 1`. For multiple dimensions, apply the index
117
+ rule along every dimension and sum over all kernel positions. Convolver accepts
118
+ real inputs only, so correlation has no visible complex-conjugation step.
119
+
120
+ ## Choose values beyond the edges
121
+
122
+ `boundary:` supplies values outside the stored signal. For a signal `a b c d`,
123
+ the following table shows examples immediately before and after it:
124
+
125
+ | Boundary | Before | Stored signal | After |
126
+ | --- | --- | --- | --- |
127
+ | `:constant` (default) | `k k k k` | `a b c d` | `k k k k` |
128
+ | `:nearest` | `a a a a` | `a b c d` | `d d d d` |
129
+ | `:reflect` | `d c b a` | `a b c d` | `d c b a` |
130
+ | `:mirror` | `d c b` | `a b c d` | `c b a` |
131
+ | `:wrap` | `a b c d` | `a b c d` | `a b c d` |
132
+
133
+ Reflect repeats the edge sample; mirror does not. Wrap treats the signal as
134
+ repeating. These rules work along every dimension, including dimensions of
135
+ length one and extensions wider than the stored signal.
136
+
137
+ For constant boundaries, `fill_value:` sets `k` and must be a real numeric
138
+ value. Omitting it uses zero. For all other boundaries, omit `fill_value:`
139
+ entirely: even an explicit `fill_value: 0` is rejected.
140
+
141
+ Some generated method signatures show `UNSPECIFIED_FILL`. This is Convolver's
142
+ internal marker for an omitted argument, not a value callers should pass. It
143
+ lets Convolver distinguish omission from an explicitly supplied zero.
144
+
145
+ The permitted combinations are:
146
+
147
+ | Mode | Boundary | Fill | Origin |
148
+ | --- | --- | --- | --- |
149
+ | `:valid` | `:constant` only | Omitted or zero | Zero |
150
+ | `:same` | Any listed boundary | Real numeric for constant; omit for others | Within the range below |
151
+ | `:full` | `:constant` only | Omitted or real numeric | Zero |
152
+
153
+ Mode and boundary names must be the symbols shown, not strings.
154
+
155
+ ## Align the kernel
156
+
157
+ `origin:` adjusts kernel alignment in same mode. Supply an integer for all
158
+ dimensions, or an array containing one integer per dimension. The default is
159
+ zero. For a kernel dimension of length `K`:
160
+
161
+ ```text
162
+ anchor = floor(K / 2) + origin
163
+ ```
164
+
165
+ The anchor must be a kernel index: `0 <= anchor < K`. For example, a kernel of
166
+ length three allows origins -1, 0 and 1; length four allows -2, -1, 0 and 1.
167
+ Valid and full modes require zero origins.
168
+
169
+ For a same-mode output at position `i`, with boundary values supplied as above:
170
+
171
+ ```text
172
+ convolution[i] = sum_j extended_signal[i + anchor - j] * kernel[j]
173
+ correlation[i] = sum_j extended_signal[i + j - anchor] * kernel[j]
174
+ ```
175
+
176
+ With origin zero, odd-length kernels use the same alignment for both operations.
177
+ For even-length kernels, correlation needs the extra boundary sample before the
178
+ signal, and convolution needs it after. Increasing the origin moves convolution
179
+ windows toward higher signal indices and correlation windows toward lower ones.
180
+
181
+ ## Interpret estimates and failures
182
+
183
+ `predict_convolve_basic_time`, `predict_convolve_fft_time`,
184
+ `predict_correlate_basic_time` and `predict_correlate_fft_time` accept the same
185
+ inputs and options as the calculation methods. They return comparative cost
186
+ estimates, not measured elapsed times. Smaller estimates suggest less work.
187
+ They do not execute the convolution or allocate its result array.
188
+
189
+ The estimates account for input conversion, boundary preparation and periodic
190
+ kernel folding. Ranks one through three use calibrated profiles for arm64 macOS
191
+ and x86_64/aarch64 Linux. Other platforms and higher ranks use a general
192
+ heuristic. These profiles describe broad platform families, not measurements
193
+ of the caller's particular CPU. Automatic selection can change as estimates
194
+ improve.
195
+
196
+ Invalid inputs or option combinations raise `ArgumentError`. Calculations and
197
+ estimators reject unrepresentable planned dimensions or buffers with
198
+ `RangeError`. FFT checks include conservative native integer and working-buffer
199
+ limits. Automatic calculation can use direct execution if FFT exceeds its
200
+ limits and the direct path is still valid.
201
+
202
+ These checks do not reserve RAM or guarantee enough memory is available.
203
+ Ordinary allocation failures still raise `NoMemoryError`.
204
+
205
+ For older applications, see the [README](../README.md) sections on the version 3
206
+ precision migration and version 2 operation-name migration.
@@ -0,0 +1,30 @@
1
+ # Convolver terminology
2
+
3
+ This glossary explains the words used in the [calculation rules](rules.md) and
4
+ API reference. See [README](../README.md) for installation and usage.
5
+
6
+ | Term | Meaning in Convolver |
7
+ | --- | --- |
8
+ | Signal | The input data being processed: for example, a sequence of readings or a grid of pixel values. It is the first argument. |
9
+ | Kernel | The second input array. Its values supply the weights multiplied with a window of signal values. |
10
+ | Window | The signal positions paired with the kernel to calculate one output value. A window can include values supplied by a boundary rule. |
11
+ | Convolution | Sliding multiplication and addition with the kernel reversed within each window. Call `convolve`; do not reverse the supplied kernel yourself. |
12
+ | Cross-correlation | Sliding multiplication and addition with the kernel in its stored order. Call `correlate`. It can measure agreement with a pattern; this is not a normalized correlation coefficient. |
13
+ | Dimension, axis, rank | A sequence has one dimension; a grid has two. An axis is one of those dimensions, and rank is their count. Rank here is not matrix rank from linear algebra. |
14
+ | Shape | The size along each dimension. Shape `[3, 5]` describes a grid with three rows and five columns, containing 15 values. |
15
+ | Scalar | A single value held in a zero-dimensional Numo array. Its shape is `[]`, unlike a one-element sequence with shape `[1]`. |
16
+ | Output extent | The size of the returned array along each dimension. `mode:` selects valid, same or full output. |
17
+ | Boundary, extension | A rule supplying values beyond the stored signal's edges. Extension does not modify the input array. |
18
+ | Fill value | The value supplied outside the signal for `boundary: :constant`. It defaults to zero and must be omitted for other boundaries. |
19
+ | Anchor, origin | The anchor is the kernel index used for same-mode alignment. The origin shifts that index from `floor(kernel_size / 2)` along each dimension. |
20
+ | Dtype | The class describing how array values are stored. Convolver returns `Numo::SFloat` (single precision) or `Numo::DFloat` (double precision). |
21
+ | Promotion | Automatic selection of a floating type from both inputs. DFloat wins if either input maps to it; otherwise SFloat is used. |
22
+ | Precision, rounding | Floating types retain a limited number of significant digits. Rounding replaces a value with a nearby representable value; double precision retains more digits than single precision. |
23
+ | Tolerance | An allowed numerical difference when comparing results. It accounts for rounding; suitable values depend on the scale of the data and the precision used. |
24
+ | View | An array referring to selected or rearranged values of another array, such as a slice or reversed sequence. Convolver uses the view's logical order. |
25
+ | Direct calculation | Computing the sliding products and sums directly. Convolver implements this path in its native extension. |
26
+ | FFT | Fast Fourier transform: a way to express data in frequency components. The FFT path uses transforms to calculate the same convolution or correlation, with potentially different rounding and memory use. |
27
+ | Working buffer | Temporary storage used during calculation. Its size and precision can differ from the returned array. |
28
+ | Folding | Adding kernel values that land on the same periodic position. This is preparation used for wrap boundaries, not a change to the stored input. |
29
+ | Relative cost, heuristic | An approximate comparison used to choose a calculation path. It is not a measured duration or a guarantee of which path will be fastest. |
30
+ | Native limit | A size or arithmetic limit of the underlying native implementation. Passing these checks does not guarantee that physical memory is available. |
@@ -2,7 +2,7 @@
2
2
 
3
3
  require 'convolver/buffer_limits'
4
4
 
5
- # Internal allocation and arithmetic checks for the PocketFFT paths.
5
+ # @!macro convolver_overview
6
6
  module Convolver
7
7
  # Only FFT representability failures permit automatic direct fallback.
8
8
  # @private
@@ -8,7 +8,7 @@ require 'convolver/operation_shapes'
8
8
  require 'convolver/signal_extension'
9
9
  require 'convolver/fft_buffer_plan'
10
10
 
11
- # Internal planning and extension support for Convolver's public operations.
11
+ # @!macro convolver_overview
12
12
  module Convolver
13
13
  # Distinguishes an omitted fill_value keyword from an explicitly supplied
14
14
  # value. This lets non-constant boundaries reject even an explicit zero.
@@ -2,5 +2,5 @@
2
2
 
3
3
  module Convolver
4
4
  # Current gem version.
5
- VERSION = '4.0.0'
5
+ VERSION = '4.0.1'
6
6
  end
data/lib/convolver.rb CHANGED
@@ -5,7 +5,26 @@ require 'numo/pocketfft'
5
5
  require 'convolver/convolver'
6
6
  require 'convolver/version'
7
7
 
8
- # Mathematical convolution and cross-correlation for Numo::NArray values.
8
+ # @!macro [new] convolver_overview
9
+ # Combine numeric sequences or grids with a kernel of weights.
10
+ #
11
+ # Start with {.convolve} for mathematical convolution or {.correlate} for
12
+ # cross-correlation. Both slide a window over the signal, multiply paired
13
+ # values and add the products. Convolution reverses the kernel within each
14
+ # window; correlation keeps its stored order. Pass the original kernel to
15
+ # either method: Convolver handles the orientation.
16
+ #
17
+ # All calculation methods return a new Numo::SFloat or Numo::DFloat array.
18
+ # Inputs are not modified. The automatic methods choose a calculation path;
19
+ # explicit direct and FFT methods are available for comparing performance.
20
+ #
21
+ # @example Compare the first complete window
22
+ # signal = Numo::DFloat[1, 2, 4, 8, 16]
23
+ # kernel = Numo::DFloat[1, 2, 3]
24
+ # Convolver.convolve(signal, kernel).to_a # => [11.0, 22.0, 44.0]
25
+ # Convolver.correlate(signal, kernel).to_a # => [17.0, 34.0, 68.0]
26
+ # @see file:docs/rules.md Complete input and calculation rules
27
+ # @see file:docs/terminology.md Terminology
9
28
  module Convolver
10
29
  # Maximum number of dimensions supported by the implementations.
11
30
  MAX_RANK = 16
@@ -17,117 +36,209 @@ module Convolver
17
36
  require 'convolver/operation_execution'
18
37
 
19
38
  class << self
20
- # Chooses the likely fastest mathematical convolution implementation.
39
+ # Calculate mathematical convolution, choosing a likely faster calculation path.
21
40
  #
22
- # Results use the dtype selected from both inputs, unless dtype overrides it.
23
- # Inputs must be nonempty arrays of equal rank, with at most {MAX_RANK}
24
- # dimensions. Scalars multiply directly.
25
- # In valid mode the kernel must fit inside the signal in every dimension.
26
- # Full mode permits only constant boundaries; nonconstant boundaries and
27
- # nonzero origins are supported only in same mode.
41
+ # Each result adds the products from one signal window, with the kernel
42
+ # reversed within that window. By default, only complete windows are returned.
43
+ # Use mode: :same to keep the signal's shape, or mode: :full to include
44
+ # partly overlapping positions at both ends of each dimension.
28
45
  #
29
- # @param signal [Numo::NArray] input signal
30
- # @param kernel [Numo::NArray] convolution kernel
31
- # @param mode [:valid, :same, :full] output extent (default: :valid)
46
+ # Inputs must be nonempty supported Numo arrays with equal numbers of
47
+ # dimensions, up to {MAX_RANK}. Two zero-dimensional arrays multiply directly.
48
+ # Both input types determine the floating result type unless dtype overrides
49
+ # it. See the rules below for the exact supported classes and combinations.
50
+ #
51
+ # UNSPECIFIED_FILL in the signature means the keyword was omitted. For a
52
+ # constant boundary this uses zero; other boundaries require omission and
53
+ # reject even an explicit fill_value: 0. Do not pass the internal marker.
54
+ #
55
+ # @param signal [Numo::NArray] sequence or grid to process
56
+ # @param kernel [Numo::NArray] weights, supplied in their original order
57
+ # @param mode [:valid, :same, :full] which output positions to return
32
58
  # @param boundary [:constant, :nearest, :reflect, :mirror, :wrap]
33
- # signal extension policy (default: :constant)
34
- # @param fill_value [Numeric] constant boundary value (default: 0);
35
- # valid mode requires zero, and other boundaries reject this option
36
- # @param origin [Integer, Array<Integer>] offset from the kernel midpoint,
37
- # shared across axes or one per axis (default: 0)
38
- # @param dtype [Class, nil] Numo::SFloat or Numo::DFloat; nil promotes both inputs.
39
- # Integer types of 8/16 bits select SFloat; 32/64 bits select DFloat.
40
- # Only these concrete integer and floating classes are supported.
41
- # @return [Numo::SFloat, Numo::DFloat] mathematical convolution result
59
+ # how to supply values beyond the signal's edges
60
+ # @param fill_value [Numeric] real value outside a constant boundary;
61
+ # omission uses zero. Valid mode requires zero; other boundaries reject
62
+ # this keyword even when explicitly zero. The internal default marker
63
+ # denotes omission and is not a caller-supplied value.
64
+ # @param origin [Integer, Array<Integer>] shift from the kernel midpoint,
65
+ # shared across dimensions or one integer per dimension; same mode only
66
+ # permits a nonzero shift, within the kernel's index range
67
+ # @param dtype [Class, nil] Numo::SFloat or Numo::DFloat; nil chooses from
68
+ # both inputs. SFloat and 8/16-bit integers select SFloat; DFloat and
69
+ # 32/64-bit integers select DFloat. The wider candidate wins.
70
+ # @return [Numo::SFloat, Numo::DFloat] a new mathematical convolution result
42
71
  # @raise [ArgumentError] if inputs or options are invalid
43
- # @raise [RangeError] if planned dimensions, buffers or FFT arithmetic exceed native limits
72
+ # @raise [RangeError] if no usable calculation path fits native size limits
73
+ # @raise [NoMemoryError] if memory allocation fails despite passing size checks
74
+ # @example Keep the signal size and reflect at its edges
75
+ # signal = Numo::DFloat[1, 2, 4, 8, 16]
76
+ # kernel = Numo::DFloat.ones(3) / 3
77
+ # result = Convolver.convolve(signal, kernel, mode: :same, boundary: :reflect)
78
+ # result.shape # => [5]
79
+ # result.to_a.map { |value| value.round(3) } # => [1.333, 2.333, 4.667, 9.333, 13.333]
80
+ # @see file:docs/rules.md Supported arrays, precision, modes and alignment
44
81
  def convolve(signal, kernel, mode: :valid, boundary: :constant,
45
82
  fill_value: UNSPECIFIED_FILL, origin: 0, dtype: nil)
46
83
  execution(:convolution, signal, kernel, mode:, boundary:, fill_value:, origin:, dtype:).automatic
47
84
  end
48
85
 
49
- # Chooses the likely fastest cross-correlation implementation.
50
- # Uses the same input and option rules as {.convolve}, without reversing
51
- # the kernel's orientation.
86
+ # Compare a signal with a kernel in its stored order using cross-correlation.
87
+ #
88
+ # Each result is the sum of paired products from one window. This is useful
89
+ # for matching a pattern; it is not a normalized correlation coefficient.
90
+ # Chooses a likely faster path, using the same input/option rules as {.convolve}.
91
+ # Even-length kernels have different same-mode alignment for the two operations;
92
+ # see the reference rules before comparing their output positions.
52
93
  # @param (see convolve)
53
- # @return [Numo::SFloat, Numo::DFloat] cross-correlation result
94
+ # @return [Numo::SFloat, Numo::DFloat] a new cross-correlation result
54
95
  # @raise (see convolve)
96
+ # @example Compare three complete windows with an asymmetric pattern
97
+ # signal = Numo::DFloat[1, 2, 4, 8, 16]
98
+ # kernel = Numo::DFloat[1, 2, 3]
99
+ # Convolver.correlate(signal, kernel).to_a # => [17.0, 34.0, 68.0]
100
+ # @see file:docs/rules.md Complete input and calculation rules
55
101
  def correlate(signal, kernel, mode: :valid, boundary: :constant,
56
102
  fill_value: UNSPECIFIED_FILL, origin: 0, dtype: nil)
57
103
  execution(:correlation, signal, kernel, mode:, boundary:, fill_value:, origin:, dtype:).automatic
58
104
  end
59
105
 
60
- # Uses the direct native mathematical convolution implementation.
106
+ # Calculate convolution directly from the sliding products and sums.
107
+ #
108
+ # Products and accumulation use the selected result precision. This method
109
+ # always uses the native direct path. For automatic selection use {.convolve}.
61
110
  # @param (see convolve)
62
- # @return [Numo::SFloat, Numo::DFloat] mathematical convolution result
63
- # @raise (see convolve)
111
+ # @return [Numo::SFloat, Numo::DFloat] a new mathematical convolution result
112
+ # @raise [ArgumentError] if inputs or options are invalid
113
+ # @raise [RangeError] if direct dimensions or buffers exceed native limits
114
+ # @raise [NoMemoryError] if memory allocation fails despite passing size checks
115
+ # @example Request single-precision input conversion and results
116
+ # signal = Numo::DFloat[1, 2, 4]
117
+ # result = Convolver.convolve_basic(signal, Numo::DFloat[1, 2], dtype: Numo::SFloat)
118
+ # result.to_a # => [4.0, 8.0]
119
+ # @see file:docs/rules.md Shared options and precision rules
64
120
  def convolve_basic(signal, kernel, mode: :valid, boundary: :constant,
65
121
  fill_value: UNSPECIFIED_FILL, origin: 0, dtype: nil)
66
122
  execution(:convolution, signal, kernel, mode:, boundary:, fill_value:, origin:, dtype:).basic
67
123
  end
68
124
 
69
- # Uses the direct native cross-correlation implementation.
125
+ # Calculate cross-correlation directly from the sliding products and sums.
126
+ #
127
+ # Keeps the kernel in stored order and uses the selected precision for
128
+ # products and accumulation. For automatic selection use {.correlate}.
70
129
  # @param (see convolve)
71
- # @return [Numo::SFloat, Numo::DFloat] cross-correlation result
72
- # @raise (see convolve)
130
+ # @return [Numo::SFloat, Numo::DFloat] a new cross-correlation result
131
+ # @raise (see convolve_basic)
132
+ # @example Use an asymmetric kernel without reversing it
133
+ # Convolver.correlate_basic(Numo::DFloat[1, 2, 4], Numo::DFloat[1, 2]).to_a
134
+ # # => [5.0, 10.0]
135
+ # @see file:docs/rules.md Shared options and alignment rules
73
136
  def correlate_basic(signal, kernel, mode: :valid, boundary: :constant,
74
137
  fill_value: UNSPECIFIED_FILL, origin: 0, dtype: nil)
75
138
  execution(:correlation, signal, kernel, mode:, boundary:, fill_value:, origin:, dtype:).basic
76
139
  end
77
140
 
78
- # Uses PocketFFT to calculate mathematical convolution.
141
+ # Calculate convolution using PocketFFT transforms.
142
+ #
143
+ # FFT working buffers use double precision for either result dtype. The
144
+ # returned shape and class follow the same rules as {.convolve}; rounding
145
+ # and propagation of non-finite values can differ from the direct path.
79
146
  # @param (see convolve)
80
- # @return [Numo::SFloat, Numo::DFloat] mathematical convolution result
81
- # @raise (see convolve)
147
+ # @return [Numo::SFloat, Numo::DFloat] a new mathematical convolution result
148
+ # @raise [ArgumentError] if inputs or options are invalid
149
+ # @raise [RangeError] if FFT dimensions, buffers or arithmetic exceed native limits
150
+ # @raise [NoMemoryError] if memory allocation fails despite passing size checks
151
+ # @example Include every partly overlapping position
152
+ # result = Convolver.convolve_fft(Numo::DFloat[1, 2, 3], Numo::DFloat[1, 2], mode: :full)
153
+ # expected = Numo::DFloat[1, 4, 7, 6]
154
+ # (result - expected).abs.max < 1e-12 # => true
155
+ # @see file:docs/rules.md Shared options, precision and memory limits
82
156
  def convolve_fft(signal, kernel, mode: :valid, boundary: :constant,
83
157
  fill_value: UNSPECIFIED_FILL, origin: 0, dtype: nil)
84
158
  execution(:convolution, signal, kernel, mode:, boundary:, fill_value:, origin:, dtype:).fft
85
159
  end
86
160
 
87
- # Uses PocketFFT to calculate cross-correlation.
161
+ # Calculate cross-correlation using PocketFFT transforms.
162
+ #
163
+ # Keeps the kernel in stored order. Working buffers use double precision;
164
+ # the returned dtype is selected from the inputs or the explicit override.
165
+ # Compare results with a tolerance, as for {.convolve_fft}.
88
166
  # @param (see convolve)
89
- # @return [Numo::SFloat, Numo::DFloat] cross-correlation result
90
- # @raise (see convolve)
167
+ # @return [Numo::SFloat, Numo::DFloat] a new cross-correlation result
168
+ # @raise (see convolve_fft)
169
+ # @example Check the result with a floating-point tolerance
170
+ # result = Convolver.correlate_fft(Numo::DFloat[1, 2, 4], Numo::DFloat[1, 2])
171
+ # (result - Numo::DFloat[5, 10]).abs.max < 1e-12 # => true
172
+ # @see file:docs/rules.md Shared options, precision and memory limits
91
173
  def correlate_fft(signal, kernel, mode: :valid, boundary: :constant,
92
174
  fill_value: UNSPECIFIED_FILL, origin: 0, dtype: nil)
93
175
  execution(:correlation, signal, kernel, mode:, boundary:, fill_value:, origin:, dtype:).fft
94
176
  end
95
177
 
96
178
  # Estimates the relative cost of {.convolve_fft}.
97
- # This is a heuristic for algorithm selection, not a timing measurement.
179
+ #
180
+ # Returns a rough comparative cost, not elapsed seconds. Includes input
181
+ # conversion and boundary preparation without running convolution or
182
+ # allocating its result. Profiles describe platform families, not a measured
183
+ # speed for this particular CPU. Compare with {.predict_convolve_basic_time}.
98
184
  # @param (see convolve)
99
- # @return [Float] machine-specific relative cost estimate
100
- # @raise (see convolve)
185
+ # @return [Float] estimated relative cost on the selected platform profile
186
+ # @raise [ArgumentError] if inputs or options are invalid
187
+ # @raise [RangeError] if the planned FFT path exceeds native limits
188
+ # @example Obtain an estimate without calculating a result array
189
+ # cost = Convolver.predict_convolve_fft_time(Numo::DFloat.ones(32), Numo::DFloat.ones(3))
190
+ # cost.finite? && cost >= 0 # => true
191
+ # @see file:docs/rules.md Interpretation and limits of estimates
101
192
  def predict_convolve_fft_time(signal, kernel, mode: :valid, boundary: :constant,
102
193
  fill_value: UNSPECIFIED_FILL, origin: 0, dtype: nil)
103
194
  execution(:convolution, signal, kernel, mode:, boundary:, fill_value:, origin:, dtype:).fft_time
104
195
  end
105
196
 
106
197
  # Estimates the relative cost of {.correlate_fft}.
107
- # This is a heuristic for algorithm selection, not a timing measurement.
198
+ # Returns a comparative cost without executing correlation or allocating its
199
+ # result. The estimate includes preparation and is not a timing measurement.
200
+ # Compare with {.predict_correlate_basic_time} for the same inputs/options.
108
201
  # @param (see convolve)
109
- # @return [Float] machine-specific relative cost estimate
110
- # @raise (see convolve)
202
+ # @return [Float] estimated relative cost on the selected platform profile
203
+ # @raise (see predict_convolve_fft_time)
204
+ # @example Estimate periodic correlation work
205
+ # cost = Convolver.predict_correlate_fft_time(Numo::DFloat.ones(32), Numo::DFloat.ones(3),
206
+ # mode: :same, boundary: :wrap)
207
+ # cost.finite? && cost >= 0 # => true
208
+ # @see file:docs/rules.md Interpretation and limits of estimates
111
209
  def predict_correlate_fft_time(signal, kernel, mode: :valid, boundary: :constant,
112
210
  fill_value: UNSPECIFIED_FILL, origin: 0, dtype: nil)
113
211
  execution(:correlation, signal, kernel, mode:, boundary:, fill_value:, origin:, dtype:).fft_time
114
212
  end
115
213
 
116
214
  # Estimates the relative cost of {.convolve_basic}.
117
- # This is a heuristic for algorithm selection, not a timing measurement.
215
+ # Returns a comparative cost without executing convolution or allocating its
216
+ # result. Smaller costs suggest less work, not a guaranteed elapsed time.
217
+ # Compare with {.predict_convolve_fft_time} for the same inputs/options.
118
218
  # @param (see convolve)
119
- # @return [Float] machine-specific relative cost estimate
120
- # @raise (see convolve)
219
+ # @return [Float] estimated relative cost on the selected platform profile
220
+ # @raise [ArgumentError] if inputs or options are invalid
221
+ # @raise [RangeError] if the planned direct path exceeds native limits
222
+ # @example Estimate direct convolution work
223
+ # cost = Convolver.predict_convolve_basic_time(Numo::DFloat.ones(32), Numo::DFloat.ones(3))
224
+ # cost.finite? && cost >= 0 # => true
225
+ # @see file:docs/rules.md Interpretation and limits of estimates
121
226
  def predict_convolve_basic_time(signal, kernel, mode: :valid, boundary: :constant,
122
227
  fill_value: UNSPECIFIED_FILL, origin: 0, dtype: nil)
123
228
  execution(:convolution, signal, kernel, mode:, boundary:, fill_value:, origin:, dtype:).basic_time
124
229
  end
125
230
 
126
231
  # Estimates the relative cost of {.correlate_basic}.
127
- # This is a heuristic for algorithm selection, not a timing measurement.
232
+ # Returns a comparative cost without executing correlation or allocating its
233
+ # result. The estimate includes preparation and is not a timing measurement.
234
+ # Compare with {.predict_correlate_fft_time} for the same inputs/options.
128
235
  # @param (see convolve)
129
- # @return [Float] machine-specific relative cost estimate
130
- # @raise (see convolve)
236
+ # @return [Float] estimated relative cost on the selected platform profile
237
+ # @raise (see predict_convolve_basic_time)
238
+ # @example Estimate direct correlation work
239
+ # cost = Convolver.predict_correlate_basic_time(Numo::DFloat.ones(32), Numo::DFloat.ones(3))
240
+ # cost.finite? && cost >= 0 # => true
241
+ # @see file:docs/rules.md Interpretation and limits of estimates
131
242
  def predict_correlate_basic_time(signal, kernel, mode: :valid, boundary: :constant,
132
243
  fill_value: UNSPECIFIED_FILL, origin: 0, dtype: nil)
133
244
  execution(:correlation, signal, kernel, mode:, boundary:, fill_value:, origin:, dtype:).basic_time
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: convolver
3
3
  version: !ruby/object:Gem::Version
4
- version: 4.0.0
4
+ version: 4.0.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Neil Slater
@@ -61,6 +61,8 @@ files:
61
61
  - CHANGELOG.md
62
62
  - LICENSE.txt
63
63
  - README.md
64
+ - docs/rules.md
65
+ - docs/terminology.md
64
66
  - ext/convolver/convolve_raw.c
65
67
  - ext/convolver/convolve_raw.h
66
68
  - ext/convolver/convolver.c