convolver 3.0.1 → 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.
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,124 +36,220 @@ 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 single precision. Inputs must be nonempty arrays of equal
23
- # rank, with at most {MAX_RANK} dimensions. Scalars multiply directly.
24
- # In valid mode the kernel must fit inside the signal in every dimension.
25
- # Full mode permits only constant boundaries; nonconstant boundaries and
26
- # 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.
27
45
  #
28
- # @param signal [Numo::NArray] input signal
29
- # @param kernel [Numo::NArray] convolution kernel
30
- # @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
31
58
  # @param boundary [:constant, :nearest, :reflect, :mirror, :wrap]
32
- # signal extension policy (default: :constant)
33
- # @param fill_value [Numeric] constant boundary value (default: 0);
34
- # valid mode requires zero, and other boundaries reject this option
35
- # @param origin [Integer, Array<Integer>] offset from the kernel midpoint,
36
- # shared across axes or one per axis (default: 0)
37
- # @return [Numo::SFloat] 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
38
71
  # @raise [ArgumentError] if inputs or options are invalid
39
- # @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
40
81
  def convolve(signal, kernel, mode: :valid, boundary: :constant,
41
- fill_value: UNSPECIFIED_FILL, origin: 0)
42
- execution(:convolution, signal, kernel, mode:, boundary:, fill_value:, origin:).automatic(self)
82
+ fill_value: UNSPECIFIED_FILL, origin: 0, dtype: nil)
83
+ execution(:convolution, signal, kernel, mode:, boundary:, fill_value:, origin:, dtype:).automatic
43
84
  end
44
85
 
45
- # Chooses the likely fastest cross-correlation implementation.
46
- # Uses the same input and option rules as {.convolve}, without reversing
47
- # 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.
48
93
  # @param (see convolve)
49
- # @return [Numo::SFloat] cross-correlation result
94
+ # @return [Numo::SFloat, Numo::DFloat] a new cross-correlation result
50
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
51
101
  def correlate(signal, kernel, mode: :valid, boundary: :constant,
52
- fill_value: UNSPECIFIED_FILL, origin: 0)
53
- execution(:correlation, signal, kernel, mode:, boundary:, fill_value:, origin:).automatic(self)
102
+ fill_value: UNSPECIFIED_FILL, origin: 0, dtype: nil)
103
+ execution(:correlation, signal, kernel, mode:, boundary:, fill_value:, origin:, dtype:).automatic
54
104
  end
55
105
 
56
- # 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}.
57
110
  # @param (see convolve)
58
- # @return [Numo::SFloat] mathematical convolution result
59
- # @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
60
120
  def convolve_basic(signal, kernel, mode: :valid, boundary: :constant,
61
- fill_value: UNSPECIFIED_FILL, origin: 0)
62
- execution(:convolution, signal, kernel, mode:, boundary:, fill_value:, origin:).basic
121
+ fill_value: UNSPECIFIED_FILL, origin: 0, dtype: nil)
122
+ execution(:convolution, signal, kernel, mode:, boundary:, fill_value:, origin:, dtype:).basic
63
123
  end
64
124
 
65
- # 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}.
66
129
  # @param (see convolve)
67
- # @return [Numo::SFloat] cross-correlation result
68
- # @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
69
136
  def correlate_basic(signal, kernel, mode: :valid, boundary: :constant,
70
- fill_value: UNSPECIFIED_FILL, origin: 0)
71
- execution(:correlation, signal, kernel, mode:, boundary:, fill_value:, origin:).basic
137
+ fill_value: UNSPECIFIED_FILL, origin: 0, dtype: nil)
138
+ execution(:correlation, signal, kernel, mode:, boundary:, fill_value:, origin:, dtype:).basic
72
139
  end
73
140
 
74
- # 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.
75
146
  # @param (see convolve)
76
- # @return [Numo::SFloat] mathematical convolution result
77
- # @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
78
156
  def convolve_fft(signal, kernel, mode: :valid, boundary: :constant,
79
- fill_value: UNSPECIFIED_FILL, origin: 0)
80
- execution(:convolution, signal, kernel, mode:, boundary:, fill_value:, origin:).fft
157
+ fill_value: UNSPECIFIED_FILL, origin: 0, dtype: nil)
158
+ execution(:convolution, signal, kernel, mode:, boundary:, fill_value:, origin:, dtype:).fft
81
159
  end
82
160
 
83
- # 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}.
84
166
  # @param (see convolve)
85
- # @return [Numo::SFloat] cross-correlation result
86
- # @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
87
173
  def correlate_fft(signal, kernel, mode: :valid, boundary: :constant,
88
- fill_value: UNSPECIFIED_FILL, origin: 0)
89
- execution(:correlation, signal, kernel, mode:, boundary:, fill_value:, origin:).fft
174
+ fill_value: UNSPECIFIED_FILL, origin: 0, dtype: nil)
175
+ execution(:correlation, signal, kernel, mode:, boundary:, fill_value:, origin:, dtype:).fft
90
176
  end
91
177
 
92
178
  # Estimates the relative cost of {.convolve_fft}.
93
- # 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}.
94
184
  # @param (see convolve)
95
- # @return [Float] machine-specific relative cost estimate
96
- # @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
97
192
  def predict_convolve_fft_time(signal, kernel, mode: :valid, boundary: :constant,
98
- fill_value: UNSPECIFIED_FILL, origin: 0)
99
- execution(:convolution, signal, kernel, mode:, boundary:, fill_value:, origin:).fft_time
193
+ fill_value: UNSPECIFIED_FILL, origin: 0, dtype: nil)
194
+ execution(:convolution, signal, kernel, mode:, boundary:, fill_value:, origin:, dtype:).fft_time
100
195
  end
101
196
 
102
197
  # Estimates the relative cost of {.correlate_fft}.
103
- # 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.
104
201
  # @param (see convolve)
105
- # @return [Float] machine-specific relative cost estimate
106
- # @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
107
209
  def predict_correlate_fft_time(signal, kernel, mode: :valid, boundary: :constant,
108
- fill_value: UNSPECIFIED_FILL, origin: 0)
109
- execution(:correlation, signal, kernel, mode:, boundary:, fill_value:, origin:).fft_time
210
+ fill_value: UNSPECIFIED_FILL, origin: 0, dtype: nil)
211
+ execution(:correlation, signal, kernel, mode:, boundary:, fill_value:, origin:, dtype:).fft_time
110
212
  end
111
213
 
112
214
  # Estimates the relative cost of {.convolve_basic}.
113
- # 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.
114
218
  # @param (see convolve)
115
- # @return [Float] machine-specific relative cost estimate
116
- # @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
117
226
  def predict_convolve_basic_time(signal, kernel, mode: :valid, boundary: :constant,
118
- fill_value: UNSPECIFIED_FILL, origin: 0)
119
- execution(:convolution, signal, kernel, mode:, boundary:, fill_value:, origin:).basic_time
227
+ fill_value: UNSPECIFIED_FILL, origin: 0, dtype: nil)
228
+ execution(:convolution, signal, kernel, mode:, boundary:, fill_value:, origin:, dtype:).basic_time
120
229
  end
121
230
 
122
231
  # Estimates the relative cost of {.correlate_basic}.
123
- # 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.
124
235
  # @param (see convolve)
125
- # @return [Float] machine-specific relative cost estimate
126
- # @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
127
242
  def predict_correlate_basic_time(signal, kernel, mode: :valid, boundary: :constant,
128
- fill_value: UNSPECIFIED_FILL, origin: 0)
129
- execution(:correlation, signal, kernel, mode:, boundary:, fill_value:, origin:).basic_time
243
+ fill_value: UNSPECIFIED_FILL, origin: 0, dtype: nil)
244
+ execution(:correlation, signal, kernel, mode:, boundary:, fill_value:, origin:, dtype:).basic_time
130
245
  end
131
246
 
132
247
  private
133
248
 
134
- private :convolve_basic_valid, :correlate_basic_valid
249
+ private :convolve_basic_valid, :correlate_basic_valid, :fft_fast_shape, :fft_shape_cost
135
250
 
136
- def execution(operation, signal, kernel, mode:, boundary:, fill_value:, origin:)
137
- OperationExecution.new(operation, signal, kernel, mode:, boundary:, fill_value:, origin:)
251
+ def execution(operation, signal, kernel, mode:, boundary:, fill_value:, origin:, dtype:)
252
+ OperationExecution.new(operation, signal, kernel, mode:, boundary:, fill_value:, origin:, dtype:)
138
253
  end
139
254
  end
140
255
  end
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: 3.0.1
4
+ version: 4.0.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Neil Slater
@@ -61,24 +61,35 @@ 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
67
69
  - ext/convolver/correlate_raw.c
68
70
  - ext/convolver/correlate_raw.h
69
71
  - ext/convolver/extconf.rb
72
+ - ext/convolver/fft_shape.c
73
+ - ext/convolver/fft_shape.h
74
+ - ext/convolver/raw_config.h
75
+ - ext/convolver/raw_helpers.h
76
+ - ext/convolver/raw_loop.h
70
77
  - lib/convolver.rb
71
78
  - lib/convolver/buffer_limits.rb
72
79
  - lib/convolver/buffer_preparation.rb
73
80
  - lib/convolver/circular_fft_operation.rb
81
+ - lib/convolver/cost_profiles.rb
74
82
  - lib/convolver/fft_buffer_plan.rb
83
+ - lib/convolver/fft_cost.rb
75
84
  - lib/convolver/fft_estimator.rb
76
85
  - lib/convolver/fft_limits.rb
77
86
  - lib/convolver/linear_fft_operation.rb
87
+ - lib/convolver/operation_dtype.rb
78
88
  - lib/convolver/operation_execution.rb
79
89
  - lib/convolver/operation_options.rb
80
90
  - lib/convolver/operation_plan.rb
81
91
  - lib/convolver/operation_shapes.rb
92
+ - lib/convolver/preparation_cost.rb
82
93
  - lib/convolver/real_fft_shape.rb
83
94
  - lib/convolver/signal_extension.rb
84
95
  - lib/convolver/version.rb