Module: Convolver
- Defined in:
- lib/convolver.rb,
lib/convolver/version.rb,
ext/convolver/convolver.c
Overview
Valid cross-correlation operations for Numo::NArray values.
Constant Summary collapse
- MAX_RANK =
Maximum number of dimensions supported by the direct native implementation.
16- VERSION =
Current gem version.
'1.0.0'
Class Method Summary collapse
-
.convolve(signal, kernel) ⇒ Numo::SFloat
Chooses the likely fastest implementation for a valid cross-correlation.
-
.convolve_basic(signal, kernel) ⇒ Numo::SFloat
Calculates a valid cross-correlation using the direct native implementation.
-
.convolve_fft(signal, kernel) ⇒ Numo::SFloat
Uses PocketFFT to calculate a valid cross-correlation.
-
.convolve_fftw3(signal, kernel) ⇒ Numo::SFloat
deprecated
Deprecated.
Use Convolver.convolve_fft; Convolver no longer uses FFTW3.
-
.predict_convolve_basic_time(signal, kernel) ⇒ Float
Estimates the relative cost of Convolver.convolve_basic.
-
.predict_convolve_fft_time(signal, kernel) ⇒ Float
Estimates the relative cost of Convolver.convolve_fft.
Class Method Details
.convolve(signal, kernel) ⇒ Numo::SFloat
Chooses the likely fastest implementation for a valid cross-correlation.
The inputs must have the same rank, and the kernel must not be larger than the signal in any dimension. The result shape is:
signal.shape.zip(kernel.shape).map { |signal_size, kernel_size| signal_size - kernel_size + 1 }
25 26 27 28 29 30 31 32 33 34 35 36 |
# File 'lib/convolver.rb', line 25 def convolve(signal, kernel) validate_inputs!(signal, kernel) return convolve_basic(signal, kernel) if signal.size < 1000 || kernel.size < 100 basic_time_predicted = predict_convolve_basic_time(signal, kernel) return convolve_basic(signal, kernel) if basic_time_predicted < 0.1 fft_time_predicted = predict_convolve_fft_time(signal, kernel) return convolve_fft(signal, kernel) if fft_time_predicted < 2 * basic_time_predicted convolve_basic(signal, kernel) end |
.convolve_basic(signal, kernel) ⇒ Numo::SFloat
Calculates a valid cross-correlation using the direct native implementation.
29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 |
# File 'ext/convolver/convolver.c', line 29
static VALUE convolver_convolve_basic(VALUE self, VALUE signal, VALUE kernel) {
volatile VALUE signal_value;
volatile VALUE kernel_value;
volatile VALUE result_value;
narray_t *signal_narray;
narray_t *kernel_narray;
int rank;
int i;
int signal_shape[LARGEST_RANK];
int kernel_shape[LARGEST_RANK];
int result_shape[LARGEST_RANK];
size_t numo_result_shape[LARGEST_RANK];
(void)self;
if (!rb_obj_is_kind_of(signal, numo_cNArray) || !rb_obj_is_kind_of(kernel, numo_cNArray)) {
rb_raise(rb_eArgError, "signal and kernel must be Numo::NArray values");
}
signal_value = rb_funcall(numo_cSFloat, rb_intern("cast"), 1, signal);
kernel_value = rb_funcall(numo_cSFloat, rb_intern("cast"), 1, kernel);
GetNArray(signal_value, signal_narray);
GetNArray(kernel_value, kernel_narray);
if (signal_narray->size == 0 || kernel_narray->size == 0) {
rb_raise(rb_eArgError, "signal and kernel must not be empty");
}
if (signal_narray->ndim != kernel_narray->ndim) {
rb_raise(rb_eArgError, "signal and kernel must have equal rank");
}
if (signal_narray->ndim > LARGEST_RANK) {
rb_raise(rb_eArgError, "maximum supported rank is %d", LARGEST_RANK);
}
rank = signal_narray->ndim;
copy_shape(rank, signal_narray->shape, signal_shape);
copy_shape(rank, kernel_narray->shape, kernel_shape);
for (i = 0; i < rank; i++) {
result_shape[i] = signal_shape[i] - kernel_shape[i] + 1;
if (result_shape[i] < 1) {
rb_raise(rb_eArgError, "kernel must not be larger than signal in any dimension");
}
numo_result_shape[rank - i - 1] = (size_t)result_shape[i];
}
result_value = nary_new(numo_cSFloat, rank, numo_result_shape);
convolve_raw(
rank, signal_shape, (float *)na_get_pointer_for_read(signal_value),
rank, kernel_shape, (float *)na_get_pointer_for_read(kernel_value),
rank, result_shape, (float *)na_get_pointer_for_write(result_value)
);
return result_value;
}
|
.convolve_fft(signal, kernel) ⇒ Numo::SFloat
Uses PocketFFT to calculate a valid cross-correlation.
44 45 46 47 48 49 50 |
# File 'lib/convolver.rb', line 44 def convolve_fft(signal, kernel) validate_inputs!(signal, kernel) ranges = kernel.shape.zip(signal.shape).map { |kernel_size, signal_size| (kernel_size - 1)...signal_size } full_convolution = Numo::Pocketfft.fftconvolve(signal, kernel.reverse) Numo::SFloat.cast(full_convolution[*ranges]) end |
.convolve_fftw3(signal, kernel) ⇒ Numo::SFloat
Use convolve_fft; Convolver no longer uses FFTW3.
Compatibility alias for the former FFTW3-backed implementation.
56 57 58 59 |
# File 'lib/convolver.rb', line 56 def convolve_fftw3(signal, kernel) warn 'Convolver.convolve_fftw3 is deprecated; use .convolve_fft instead', uplevel: 1 convolve_fft(signal, kernel) end |
.predict_convolve_basic_time(signal, kernel) ⇒ Float
Estimates the relative cost of convolve_basic.
77 78 79 80 81 |
# File 'lib/convolver.rb', line 77 def predict_convolve_basic_time(signal, kernel) validate_inputs!(signal, kernel) outputs = result_shape(signal.shape, kernel.shape).inject(:*) 4.54e-12 * (outputs * signal.size * kernel.size) end |
.predict_convolve_fft_time(signal, kernel) ⇒ Float
Estimates the relative cost of convolve_fft.
66 67 68 69 70 |
# File 'lib/convolver.rb', line 66 def predict_convolve_fft_time(signal, kernel) validate_inputs!(signal, kernel) output_size = result_shape(signal.shape, kernel.shape).inject(:*) 16 * 4.55e-08 * output_size * Math.log(output_size) end |