convolver 0.3.2 → 1.0.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 +5 -5
- data/CHANGELOG.md +47 -0
- data/README.md +72 -39
- data/ext/convolver/convolve_raw.c +21 -7
- data/ext/convolver/convolve_raw.h +5 -2
- data/ext/convolver/convolver.c +63 -93
- data/ext/convolver/extconf.rb +42 -23
- data/lib/convolver/version.rb +4 -1
- data/lib/convolver.rb +91 -94
- metadata +28 -115
- data/.gitignore +0 -19
- data/.travis.yml +0 -19
- data/Gemfile +0 -4
- data/Rakefile +0 -20
- data/convolver.gemspec +0 -31
- data/ext/convolver/narray_shared.c +0 -61
- data/ext/convolver/narray_shared.h +0 -22
- data/spec/convolve_basic_spec.rb +0 -84
- data/spec/convolve_fftw3_spec.rb +0 -161
- data/spec/convolve_spec.rb +0 -49
- data/spec/helpers.rb +0 -46
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
|
-
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: 4c27f2aa98b988627391d0a9812c6b8e0a59e65cf4a506592d95f43d1a02e3ae
|
|
4
|
+
data.tar.gz: ede25a5203ed707bd3ca726eb513ceb44746bc5a04c294396d368ab3785b2c4c
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: aaf0fc6ab38eedbe06c2082b8ffaa2cdbfab45b3340aadf7c87839d28aa97dbb3be1e22a99c3aef25207b656741c69763bb177003d74e440ce7d6653be22aff8
|
|
7
|
+
data.tar.gz: 20b127fa09a8c6b449e74183da3fa91a93603bc17701b4991543de6095ae64023ef217c166d5a7ef999a241de125c7278e6f1e2b3d895f5ec0d0fe2665188bd5
|
data/CHANGELOG.md
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to this project will be documented in this file.
|
|
4
|
+
|
|
5
|
+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
6
|
+
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
|
+
|
|
8
|
+
## [1.0.0] - 2026-07-21
|
|
9
|
+
|
|
10
|
+
### Added
|
|
11
|
+
|
|
12
|
+
- Native C quality tasks for strict compiler warnings (`c:lint`), GCC/gcovr
|
|
13
|
+
coverage (`c:coverage`), and ASan/UBSan checks (`c:sanitize`).
|
|
14
|
+
- GitHub Actions reporting and downloadable HTML and Cobertura artifacts for C
|
|
15
|
+
coverage.
|
|
16
|
+
- RuboCop checks with Rake and RSpec plugins.
|
|
17
|
+
- SimpleCov reporting for the Ruby test suite.
|
|
18
|
+
- Support for Ruby 4.0 in the CI test matrix.
|
|
19
|
+
- Monthly scheduled CI runs to detect dependency and toolchain regressions.
|
|
20
|
+
- Ruby branch-coverage reporting and additional coverage of native input
|
|
21
|
+
validation.
|
|
22
|
+
|
|
23
|
+
### Changed
|
|
24
|
+
|
|
25
|
+
- **Breaking:** Replaced the unmaintained `narray` dependency with
|
|
26
|
+
`numo-narray-alt`. Public methods now accept `Numo::NArray` values and return
|
|
27
|
+
`Numo::SFloat` results instead of legacy `NArray` values.
|
|
28
|
+
- **Breaking:** Raised the minimum supported Ruby version to 3.2.
|
|
29
|
+
- Replaced the `fftw3` gem and external FFTW library with `numo-pocketfft`,
|
|
30
|
+
removing the system FFTW installation requirement.
|
|
31
|
+
- Renamed the FFT-backed implementation from `convolve_fftw3` to
|
|
32
|
+
`convolve_fft`. The old name remains as a deprecated compatibility alias.
|
|
33
|
+
- Clarified that the operations calculate valid cross-correlation and expanded
|
|
34
|
+
the README and YARD documentation for input, output, and shape behaviour.
|
|
35
|
+
- Replaced the retired Travis CI configuration with GitHub Actions.
|
|
36
|
+
- Improved gem metadata and limited packaged files to the library, native
|
|
37
|
+
extension, documentation, and licence.
|
|
38
|
+
|
|
39
|
+
### Fixed
|
|
40
|
+
|
|
41
|
+
- Reject non-Numo inputs, empty arrays, unequal ranks, kernels larger than their
|
|
42
|
+
signals, and ranks unsupported by the native implementation with descriptive
|
|
43
|
+
`ArgumentError` messages.
|
|
44
|
+
- Updated the native extension to use the maintained Numo C API and removed the
|
|
45
|
+
legacy untyped-data compatibility code.
|
|
46
|
+
|
|
47
|
+
[1.0.0]: https://github.com/neilslater/convolver/compare/v0.3.2...v1.0.0
|
data/README.md
CHANGED
|
@@ -1,66 +1,99 @@
|
|
|
1
1
|
# Convolver
|
|
2
2
|
|
|
3
|
-
[](http://travis-ci.org/neilslater/convolver)
|
|
5
|
-
[](https://coveralls.io/r/neilslater/convolver?branch=master)
|
|
6
|
-
[](http://inch-ci.org/github/neilslater/convolver)
|
|
7
|
-
[](https://codeclimate.com/github/neilslater/convolver)
|
|
3
|
+
[](https://badge.fury.io/rb/convolver)
|
|
8
4
|
|
|
9
|
-
|
|
10
|
-
|
|
5
|
+
Convolver calculates valid cross-correlations between multidimensional
|
|
6
|
+
[`Numo::NArray`](https://github.com/yoshoku/numo-narray-alt) values. It chooses
|
|
7
|
+
between a direct native implementation for smaller inputs and a
|
|
8
|
+
[`Numo::Pocketfft`](https://github.com/yoshoku/numo-pocketfft)-based
|
|
9
|
+
implementation for larger inputs.
|
|
10
|
+
|
|
11
|
+
Version 1.0 replaces the unmaintained `narray` and `fftw3` gems with
|
|
12
|
+
`numo-narray-alt` and `numo-pocketfft`.
|
|
11
13
|
|
|
12
14
|
## Installation
|
|
13
15
|
|
|
14
|
-
|
|
16
|
+
Add Convolver to your application's Gemfile:
|
|
17
|
+
|
|
18
|
+
```ruby
|
|
19
|
+
gem 'convolver'
|
|
20
|
+
```
|
|
15
21
|
|
|
16
|
-
|
|
17
|
-
See http://www.fftw.org/ for details.
|
|
22
|
+
Then run:
|
|
18
23
|
|
|
19
|
-
|
|
24
|
+
```sh
|
|
25
|
+
bundle install
|
|
26
|
+
```
|
|
20
27
|
|
|
21
|
-
|
|
28
|
+
Alternatively, install it directly:
|
|
22
29
|
|
|
23
|
-
|
|
30
|
+
```sh
|
|
31
|
+
gem install convolver
|
|
32
|
+
```
|
|
24
33
|
|
|
25
|
-
|
|
34
|
+
No external FFT library is required; PocketFFT is bundled by its Ruby gem.
|
|
26
35
|
|
|
27
|
-
|
|
36
|
+
## Usage
|
|
28
37
|
|
|
29
|
-
|
|
38
|
+
```ruby
|
|
39
|
+
require 'convolver'
|
|
30
40
|
|
|
31
|
-
|
|
41
|
+
signal = Numo::SFloat[0.3, 0.4, 0.5]
|
|
42
|
+
kernel = Numo::SFloat[1.3, -0.5]
|
|
32
43
|
|
|
33
|
-
|
|
44
|
+
Convolver.convolve(signal, kernel)
|
|
45
|
+
# => Numo::SFloat#shape=[2]
|
|
46
|
+
# [0.19, 0.27]
|
|
47
|
+
```
|
|
34
48
|
|
|
35
|
-
|
|
49
|
+
The signal and kernel must have the same rank, and the kernel must be no larger
|
|
50
|
+
than the signal in any dimension. Convolver returns only positions at which the
|
|
51
|
+
kernel overlaps the signal completely. The result size in each dimension is:
|
|
36
52
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
=> NArray.float(2): [ 0.19, 0.27 ]
|
|
53
|
+
```ruby
|
|
54
|
+
signal_size - kernel_size + 1
|
|
55
|
+
```
|
|
41
56
|
|
|
42
|
-
|
|
43
|
-
|
|
57
|
+
Inputs are converted to single-precision floats internally, and results are
|
|
58
|
+
returned as `Numo::SFloat`. Supplying `Numo::SFloat` inputs avoids conversion in
|
|
59
|
+
the direct implementation.
|
|
44
60
|
|
|
45
|
-
|
|
46
|
-
|
|
61
|
+
`Convolver.convolve` normally chooses the implementation automatically. The
|
|
62
|
+
implementations can also be called directly for application-specific
|
|
63
|
+
benchmarking:
|
|
47
64
|
|
|
48
|
-
|
|
49
|
-
|
|
65
|
+
```ruby
|
|
66
|
+
Convolver.convolve_basic(signal, kernel)
|
|
67
|
+
Convolver.convolve_fft(signal, kernel)
|
|
68
|
+
```
|
|
50
69
|
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
and large convolutions are processed using FFTW3 to convert to frequency space where convolution
|
|
54
|
-
is simpler and faster to calculate, then convert back.
|
|
70
|
+
`Convolver.convolve_fftw3` remains as a deprecated alias for `convolve_fft` to
|
|
71
|
+
ease migration from Convolver 0.x.
|
|
55
72
|
|
|
56
73
|
## Contributing
|
|
57
74
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
75
|
+
Install the development dependencies, then run the main suite:
|
|
76
|
+
|
|
77
|
+
```sh
|
|
78
|
+
bundle install
|
|
79
|
+
bundle exec rake
|
|
80
|
+
bundle exec rubocop
|
|
81
|
+
bundle exec rake c:lint
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
The Ruby specs exercise both the Ruby API and native extension. Additional
|
|
85
|
+
native-code checks are available:
|
|
86
|
+
|
|
87
|
+
```sh
|
|
88
|
+
bundle exec rake c:coverage # Requires GCC and gcovr
|
|
89
|
+
bundle exec rake c:sanitize # Requires Linux and GCC
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
`c:coverage` writes HTML and Cobertura reports under `coverage/c`. CI uploads
|
|
93
|
+
the reports as a `c-coverage` artifact. The sanitizer task uses AddressSanitizer
|
|
94
|
+
and UndefinedBehaviorSanitizer.
|
|
63
95
|
|
|
64
96
|
## Contributors
|
|
65
97
|
|
|
66
|
-
|
|
98
|
+
- [Dima Ermilov](https://github.com/adworse) contributed the original Windows
|
|
99
|
+
compilation support.
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
#include "convolve_raw.h"
|
|
4
4
|
|
|
5
|
-
inline int size_from_shape( int rank, int *shape ) {
|
|
5
|
+
static inline int size_from_shape( int rank, int *shape ) {
|
|
6
6
|
int size = 1;
|
|
7
7
|
int i;
|
|
8
8
|
for ( i = 0; i < rank; i++ ) { size *= shape[i]; }
|
|
@@ -10,15 +10,16 @@ inline int size_from_shape( int rank, int *shape ) {
|
|
|
10
10
|
}
|
|
11
11
|
|
|
12
12
|
// Sets reverse indices
|
|
13
|
-
inline void corner_reset( int rank, int *shape, int *rev_indices ) {
|
|
13
|
+
static inline void corner_reset( int rank, int *shape, int *rev_indices ) {
|
|
14
14
|
int i;
|
|
15
15
|
for ( i = 0; i < rank; i++ ) { rev_indices[i] = shape[i] - 1; }
|
|
16
16
|
return;
|
|
17
17
|
}
|
|
18
18
|
|
|
19
19
|
// Counts indices down, returns number of ranks that reset
|
|
20
|
-
inline int corner_dec( int rank, int *shape, int *rev_indices ) {
|
|
20
|
+
static inline int corner_dec( int rank, int *shape, int *rev_indices ) {
|
|
21
21
|
int i = 0;
|
|
22
|
+
(void) rank;
|
|
22
23
|
while ( ! rev_indices[i]-- ) {
|
|
23
24
|
rev_indices[i] = shape[i] - 1;
|
|
24
25
|
i++;
|
|
@@ -27,7 +28,7 @@ inline int corner_dec( int rank, int *shape, int *rev_indices ) {
|
|
|
27
28
|
}
|
|
28
29
|
|
|
29
30
|
// Generates co-increment steps by rank boundaries crossed, for the outer position as inner position is incremented by 1
|
|
30
|
-
inline void calc_co_increment( int rank, int *outer_shape, int *inner_shape, int *co_increment ) {
|
|
31
|
+
static inline void calc_co_increment( int rank, int *outer_shape, int *inner_shape, int *co_increment ) {
|
|
31
32
|
int i, factor;
|
|
32
33
|
co_increment[0] = 1; // co-increment is always 1 in lowest rank
|
|
33
34
|
factor = 1;
|
|
@@ -48,12 +49,11 @@ void convolve_raw(
|
|
|
48
49
|
int in_rank, int *in_shape, float *in_ptr,
|
|
49
50
|
int kernel_rank, int *kernel_shape, float *kernel_ptr,
|
|
50
51
|
int out_rank, int *out_shape, float *out_ptr ) {
|
|
51
|
-
int i, j,
|
|
52
|
+
int i, j, kernel_size, kernel_aligned, out_size, offset;
|
|
52
53
|
int out_co_incr[LARGEST_RANK], kernel_co_incr[LARGEST_RANK];
|
|
53
54
|
int ker_q[LARGEST_RANK], out_q[LARGEST_RANK];
|
|
54
55
|
int *kernel_co_incr_cache;
|
|
55
56
|
|
|
56
|
-
in_size = size_from_shape( in_rank, in_shape );
|
|
57
57
|
kernel_size = size_from_shape( kernel_rank, kernel_shape );
|
|
58
58
|
kernel_aligned = 4 * (kernel_size/4);
|
|
59
59
|
out_size = size_from_shape( out_rank, out_shape );
|
|
@@ -76,13 +76,16 @@ void convolve_raw(
|
|
|
76
76
|
|
|
77
77
|
// Main convolve loop
|
|
78
78
|
for ( i = 0; i < out_size; i++ ) {
|
|
79
|
+
#if CONVOLVER_USE_SSE
|
|
79
80
|
__m128 simd_x, simd_y, simd_t;
|
|
80
|
-
float t = 0.0;
|
|
81
81
|
float v[4];
|
|
82
82
|
simd_t = _mm_setzero_ps();
|
|
83
|
+
#endif
|
|
84
|
+
float t = 0.0;
|
|
83
85
|
|
|
84
86
|
offset += out_co_incr[ corner_dec( out_rank, out_shape, out_q ) ];
|
|
85
87
|
|
|
88
|
+
#if CONVOLVER_USE_SSE
|
|
86
89
|
// Use SIMD for all the aligned values in groups of 4
|
|
87
90
|
for ( j = 0; j < kernel_aligned; j +=4 ) {
|
|
88
91
|
simd_x = _mm_load_ps( kernel_ptr + j );
|
|
@@ -93,13 +96,24 @@ void convolve_raw(
|
|
|
93
96
|
simd_t = _mm_add_ps( simd_x, simd_t );
|
|
94
97
|
}
|
|
95
98
|
_mm_store_ps( v, simd_t );
|
|
99
|
+
#else
|
|
100
|
+
// SSE is unavailable on architectures such as Apple Silicon.
|
|
101
|
+
// Use the same calculation without x86-specific intrinsics.
|
|
102
|
+
for ( j = 0; j < kernel_aligned; j++ ) {
|
|
103
|
+
t += in_ptr[ offset + kernel_co_incr_cache[j] ] * kernel_ptr[j];
|
|
104
|
+
}
|
|
105
|
+
#endif
|
|
96
106
|
|
|
97
107
|
// Complete any remaining 1,2 or 3 items one at a time
|
|
98
108
|
for ( j = kernel_aligned; j < kernel_size; j++ ) {
|
|
99
109
|
t += in_ptr[ offset + kernel_co_incr_cache[j] ] * kernel_ptr[ j ];
|
|
100
110
|
}
|
|
101
111
|
|
|
112
|
+
#if CONVOLVER_USE_SSE
|
|
102
113
|
out_ptr[i] = v[0] + v[1] + v[2] + v[3] + t;
|
|
114
|
+
#else
|
|
115
|
+
out_ptr[i] = t;
|
|
116
|
+
#endif
|
|
103
117
|
}
|
|
104
118
|
|
|
105
119
|
xfree( kernel_co_incr_cache );
|
|
@@ -9,9 +9,12 @@
|
|
|
9
9
|
#define CONVOLVE_RAW_H
|
|
10
10
|
|
|
11
11
|
#include <ruby.h>
|
|
12
|
+
#if defined(__SSE__)
|
|
12
13
|
#include <xmmintrin.h>
|
|
13
|
-
#
|
|
14
|
-
|
|
14
|
+
#define CONVOLVER_USE_SSE 1
|
|
15
|
+
#else
|
|
16
|
+
#define CONVOLVER_USE_SSE 0
|
|
17
|
+
#endif
|
|
15
18
|
#define LARGEST_RANK 16
|
|
16
19
|
|
|
17
20
|
void convolve_raw(
|
data/ext/convolver/convolver.c
CHANGED
|
@@ -1,119 +1,89 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
#include <limits.h>
|
|
3
2
|
#include <ruby.h>
|
|
4
|
-
#include
|
|
5
|
-
#include <
|
|
6
|
-
#include <xmmintrin.h>
|
|
3
|
+
#include <numo/narray.h>
|
|
4
|
+
#include <numo/intern.h>
|
|
7
5
|
|
|
8
|
-
#include "narray_shared.h"
|
|
9
6
|
#include "convolve_raw.h"
|
|
10
7
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
// To hold the module object
|
|
14
|
-
VALUE Convolver = Qnil;
|
|
15
|
-
|
|
16
|
-
/* @overload fit_kernel_backwards( fft_temp_space, kernel )
|
|
17
|
-
* @!visibility private
|
|
18
|
-
* Over-writes fft_temp_space at edges with a reversed copy of kernel, in such a way that
|
|
19
|
-
* an FFTW3-based convolve has a result set in an easy-to-extract position later. This is
|
|
20
|
-
* implemented as a native extension for convenience and speed - to do this with methods provided
|
|
21
|
-
* by narray gem would take several complex steps and be inefficient.
|
|
22
|
-
* @param [NArray<sfloat>] fft_temp_space target array for pre-fft copy of kernel, is over-written
|
|
23
|
-
* @param [NArray] kernel must be same size or smaller than fft_temp_space in each dimension
|
|
24
|
-
* @return [nil]
|
|
25
|
-
*/
|
|
26
|
-
static VALUE narray_fit_backwards( VALUE self, VALUE a, VALUE b ) {
|
|
27
|
-
struct NARRAY *na_a, *na_b;
|
|
28
|
-
volatile VALUE val_a, val_b;
|
|
29
|
-
int target_rank, i;
|
|
30
|
-
int shift_by[LARGEST_RANK];
|
|
31
|
-
|
|
32
|
-
val_a = na_cast_object(a, NA_SFLOAT);
|
|
33
|
-
GetNArray( val_a, na_a );
|
|
34
|
-
|
|
35
|
-
val_b = na_cast_object(b, NA_SFLOAT);
|
|
36
|
-
GetNArray( val_b, na_b );
|
|
37
|
-
|
|
38
|
-
if ( na_a->rank != na_b->rank ) {
|
|
39
|
-
rb_raise( rb_eArgError, "narray a must have equal rank to narray b (a rank %d, b rank %d)", na_a->rank, na_b->rank );
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
if ( na_a->rank > LARGEST_RANK ) {
|
|
43
|
-
rb_raise( rb_eArgError, "exceeded maximum narray rank for convolve of %d", LARGEST_RANK );
|
|
44
|
-
}
|
|
8
|
+
static VALUE mConvolver;
|
|
45
9
|
|
|
46
|
-
|
|
10
|
+
static void copy_shape(int rank, const size_t *source, int *target) {
|
|
11
|
+
int i;
|
|
47
12
|
|
|
48
|
-
for (
|
|
49
|
-
if (
|
|
50
|
-
rb_raise(
|
|
13
|
+
for (i = 0; i < rank; i++) {
|
|
14
|
+
if (source[rank - i - 1] > INT_MAX) {
|
|
15
|
+
rb_raise(rb_eArgError, "array dimension exceeds native implementation limit");
|
|
51
16
|
}
|
|
52
|
-
|
|
17
|
+
target[i] = (int)source[rank - i - 1];
|
|
53
18
|
}
|
|
54
|
-
|
|
55
|
-
fit_backwards_raw(
|
|
56
|
-
target_rank,
|
|
57
|
-
na_a->shape, (float*) na_a->ptr,
|
|
58
|
-
na_b->shape, (float*) na_b->ptr,
|
|
59
|
-
shift_by );
|
|
60
|
-
|
|
61
|
-
return Qnil;
|
|
62
19
|
}
|
|
63
20
|
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
*
|
|
67
|
-
*
|
|
68
|
-
*
|
|
69
|
-
*
|
|
70
|
-
*
|
|
71
|
-
* @return [NArray] result of convolving signal with kernel
|
|
21
|
+
/*
|
|
22
|
+
* Calculates a valid cross-correlation using the direct native implementation.
|
|
23
|
+
*
|
|
24
|
+
* @overload convolve_basic(signal, kernel)
|
|
25
|
+
* @param signal [Numo::NArray] input values
|
|
26
|
+
* @param kernel [Numo::NArray] correlation kernel
|
|
27
|
+
* @return [Numo::SFloat] valid cross-correlation result
|
|
72
28
|
*/
|
|
73
|
-
static VALUE
|
|
74
|
-
|
|
75
|
-
volatile VALUE
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
29
|
+
static VALUE convolver_convolve_basic(VALUE self, VALUE signal, VALUE kernel) {
|
|
30
|
+
volatile VALUE signal_value;
|
|
31
|
+
volatile VALUE kernel_value;
|
|
32
|
+
volatile VALUE result_value;
|
|
33
|
+
narray_t *signal_narray;
|
|
34
|
+
narray_t *kernel_narray;
|
|
35
|
+
int rank;
|
|
36
|
+
int i;
|
|
37
|
+
int signal_shape[LARGEST_RANK];
|
|
38
|
+
int kernel_shape[LARGEST_RANK];
|
|
39
|
+
int result_shape[LARGEST_RANK];
|
|
40
|
+
size_t numo_result_shape[LARGEST_RANK];
|
|
41
|
+
|
|
42
|
+
(void)self;
|
|
43
|
+
|
|
44
|
+
if (!rb_obj_is_kind_of(signal, numo_cNArray) || !rb_obj_is_kind_of(kernel, numo_cNArray)) {
|
|
45
|
+
rb_raise(rb_eArgError, "signal and kernel must be Numo::NArray values");
|
|
46
|
+
}
|
|
81
47
|
|
|
82
|
-
|
|
83
|
-
|
|
48
|
+
signal_value = rb_funcall(numo_cSFloat, rb_intern("cast"), 1, signal);
|
|
49
|
+
kernel_value = rb_funcall(numo_cSFloat, rb_intern("cast"), 1, kernel);
|
|
50
|
+
GetNArray(signal_value, signal_narray);
|
|
51
|
+
GetNArray(kernel_value, kernel_narray);
|
|
84
52
|
|
|
85
|
-
if (
|
|
86
|
-
rb_raise(
|
|
53
|
+
if (signal_narray->size == 0 || kernel_narray->size == 0) {
|
|
54
|
+
rb_raise(rb_eArgError, "signal and kernel must not be empty");
|
|
87
55
|
}
|
|
88
56
|
|
|
89
|
-
if (
|
|
90
|
-
rb_raise(
|
|
57
|
+
if (signal_narray->ndim != kernel_narray->ndim) {
|
|
58
|
+
rb_raise(rb_eArgError, "signal and kernel must have equal rank");
|
|
59
|
+
}
|
|
60
|
+
if (signal_narray->ndim > LARGEST_RANK) {
|
|
61
|
+
rb_raise(rb_eArgError, "maximum supported rank is %d", LARGEST_RANK);
|
|
91
62
|
}
|
|
92
63
|
|
|
93
|
-
|
|
64
|
+
rank = signal_narray->ndim;
|
|
65
|
+
copy_shape(rank, signal_narray->shape, signal_shape);
|
|
66
|
+
copy_shape(rank, kernel_narray->shape, kernel_shape);
|
|
94
67
|
|
|
95
|
-
for (
|
|
96
|
-
|
|
97
|
-
if (
|
|
98
|
-
rb_raise(
|
|
68
|
+
for (i = 0; i < rank; i++) {
|
|
69
|
+
result_shape[i] = signal_shape[i] - kernel_shape[i] + 1;
|
|
70
|
+
if (result_shape[i] < 1) {
|
|
71
|
+
rb_raise(rb_eArgError, "kernel must not be larger than signal in any dimension");
|
|
99
72
|
}
|
|
73
|
+
numo_result_shape[rank - i - 1] = (size_t)result_shape[i];
|
|
100
74
|
}
|
|
101
75
|
|
|
102
|
-
|
|
103
|
-
GetNArray( val_c, na_c );
|
|
104
|
-
|
|
76
|
+
result_value = nary_new(numo_cSFloat, rank, numo_result_shape);
|
|
105
77
|
convolve_raw(
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
78
|
+
rank, signal_shape, (float *)na_get_pointer_for_read(signal_value),
|
|
79
|
+
rank, kernel_shape, (float *)na_get_pointer_for_read(kernel_value),
|
|
80
|
+
rank, result_shape, (float *)na_get_pointer_for_write(result_value)
|
|
81
|
+
);
|
|
109
82
|
|
|
110
|
-
return
|
|
83
|
+
return result_value;
|
|
111
84
|
}
|
|
112
85
|
|
|
113
|
-
void Init_convolver() {
|
|
114
|
-
|
|
115
|
-
rb_define_singleton_method(
|
|
116
|
-
|
|
117
|
-
// private method
|
|
118
|
-
rb_define_singleton_method( Convolver, "fit_kernel_backwards", narray_fit_backwards, 2 );
|
|
86
|
+
void Init_convolver(void) {
|
|
87
|
+
mConvolver = rb_define_module("Convolver");
|
|
88
|
+
rb_define_singleton_method(mConvolver, "convolve_basic", convolver_convolve_basic, 2);
|
|
119
89
|
}
|
data/ext/convolver/extconf.rb
CHANGED
|
@@ -1,31 +1,50 @@
|
|
|
1
|
-
#
|
|
2
|
-
|
|
3
|
-
require
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'mkmf'
|
|
4
|
+
require 'numo/narray/alt'
|
|
5
|
+
|
|
6
|
+
$LOAD_PATH.each do |load_path|
|
|
7
|
+
next unless File.exist?(File.join(load_path, 'numo/numo/narray.h'))
|
|
8
|
+
|
|
9
|
+
$INCFLAGS = "-I#{File.join(load_path, 'numo')} #{$INCFLAGS}"
|
|
10
|
+
break
|
|
11
11
|
end
|
|
12
12
|
|
|
13
|
-
|
|
14
|
-
print <<-EOS
|
|
15
|
-
** configure error **
|
|
16
|
-
Header narray.h or narray_config.h is not found. If you have these files in
|
|
17
|
-
/narraydir/include, try the following:
|
|
13
|
+
abort 'numo/narray.h not found' unless have_header('numo/narray.h')
|
|
18
14
|
|
|
19
|
-
|
|
15
|
+
if RUBY_PLATFORM.match?(/mswin|cygwin|mingw/)
|
|
16
|
+
$LOAD_PATH.each do |load_path|
|
|
17
|
+
next unless File.exist?(File.join(load_path, 'numo/narray/libnarray.a'))
|
|
20
18
|
|
|
21
|
-
|
|
22
|
-
|
|
19
|
+
$LDFLAGS = "-L#{File.join(load_path, 'numo/narray')} #{$LDFLAGS}"
|
|
20
|
+
break
|
|
21
|
+
end
|
|
22
|
+
abort 'libnarray.a not found' unless have_library('narray', 'nary_new')
|
|
23
23
|
end
|
|
24
24
|
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
25
|
+
if RUBY_PLATFORM.include?('darwin') && Gem::Version.new(RUBY_VERSION) >= Gem::Version.new('3.1') &&
|
|
26
|
+
try_link('int main(void) { return 0; }', '-Wl,-undefined,dynamic_lookup')
|
|
27
|
+
$LDFLAGS << ' -Wl,-undefined,dynamic_lookup'
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
case ENV.fetch('CONVOLVER_NATIVE_MODE', 'release')
|
|
31
|
+
when 'release'
|
|
32
|
+
$CFLAGS << ' -O3 -funroll-loops'
|
|
33
|
+
when 'lint'
|
|
34
|
+
$CFLAGS << ' -std=gnu2x -O0 -g -Wall -Wextra -Wpedantic -Wformat=2 -Werror'
|
|
35
|
+
if RbConfig::CONFIG.fetch('CC').match?(/clang/) || RbConfig::CONFIG.fetch('host_os').match?(/darwin/)
|
|
36
|
+
$CFLAGS << ' -Wno-c23-extensions -Wno-strict-prototypes -Wno-unused-parameter'
|
|
37
|
+
$CFLAGS << ' -Wno-default-const-init-field-unsafe'
|
|
38
|
+
end
|
|
39
|
+
when 'coverage'
|
|
40
|
+
$CFLAGS << ' -O0 -g --coverage'
|
|
41
|
+
$LDFLAGS << ' --coverage'
|
|
42
|
+
when 'sanitize'
|
|
43
|
+
sanitizer_flags = ' -O1 -g -fsanitize=address,undefined -fno-omit-frame-pointer'
|
|
44
|
+
$CFLAGS << sanitizer_flags
|
|
45
|
+
$LDFLAGS << ' -fsanitize=address,undefined'
|
|
46
|
+
else
|
|
47
|
+
abort "Unknown CONVOLVER_NATIVE_MODE: #{ENV.fetch('CONVOLVER_NATIVE_MODE', nil)}"
|
|
28
48
|
end
|
|
29
49
|
|
|
30
|
-
|
|
31
|
-
create_makefile( 'convolver/convolver' )
|
|
50
|
+
create_makefile('convolver/convolver')
|