convolver-light 0.3.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.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: e80f99f8aa860c6a95429b390c2d3915319ec1ce740ec29c4bbf3d60253ee2ee
4
+ data.tar.gz: 47337ccc67b933083cd3cdaf517171c63477773917c2a9fa19b0584d3e35da5c
5
+ SHA512:
6
+ metadata.gz: bc24eadf42dc390dbd7ef993fab2bbffb1648b9a51619d40dc41232b760beb6f67342e5dd9b5f477ca1c9909c0b9afb588cb753a7d7d0ff350b1b5567485e04c
7
+ data.tar.gz: 12be9439142f8e25a7f69541faa5ae94f524d53da839892af4fac3af88f42261453410245bc5bfb3684595f5bcdaf8941214948c9cf890fb359be30f3821cfa1
@@ -0,0 +1,23 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ lib/convolver/*.bundle
5
+ .config
6
+ .yardoc
7
+ Gemfile.lock
8
+ InstalledFiles
9
+ _yardoc
10
+ coverage
11
+ doc/
12
+ lib/bundler/man
13
+ pkg
14
+ rdoc
15
+ spec/reports
16
+ test/tmp
17
+ test/version_tmp
18
+ tmp
19
+ benchmarks
20
+ .DS_Store
21
+ ._.DS_Store
22
+ **/.DS_Store
23
+ **/._.DS_Store
@@ -0,0 +1,10 @@
1
+ before_install:
2
+ - sudo apt-get update -qq
3
+ - sudo apt-get install -qq libfftw3-dev
4
+ - gem install bundler
5
+ language: ruby
6
+ rvm:
7
+ - "2.1.4"
8
+ - "2.2.0"
9
+ - "2.3.1"
10
+ - "2.4.1"
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in convolver.gemspec
4
+ gemspec
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2013 Neil Slater
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,34 @@
1
+ # Convolver-Light
2
+
3
+ This is native-only version of [neilslater's](https://github.com/neilslater) [Convolver Gem](https://github.com/neilslater/convolver)
4
+
5
+ FFTW3 dependency is removed, so calculations would be slow on big matrices. Use it only if you need to make a convolution with a small kernel.
6
+
7
+ All the credits to the [author](https://github.com/neilslater)
8
+
9
+ ### Installing the gem
10
+
11
+ Add this line to your application's Gemfile:
12
+
13
+ gem 'convolver-light'
14
+
15
+ And then execute:
16
+
17
+ $ bundle
18
+
19
+ Or install it yourself as:
20
+
21
+ $ gem install convolver-light
22
+
23
+ ## Usage
24
+
25
+ require 'convolver-light
26
+
27
+ Usage is exactly the same as of original gem, please refer to the [author's page](https://github.com/neilslater/convolver)
28
+
29
+ ```
30
+ a = NArray[0.3,0.4,0.5]
31
+ b = NArray[1.3, -0.5]
32
+ c = Convolver.convolve( a, b )
33
+ => NArray.float(2): [ 0.19, 0.27 ]
34
+ ```
@@ -0,0 +1,20 @@
1
+ require "bundler/gem_tasks"
2
+ require "rspec/core/rake_task"
3
+ require 'rake/extensiontask'
4
+
5
+ desc "Convolver unit tests"
6
+ RSpec::Core::RakeTask.new(:test) do |t|
7
+ t.pattern = "spec/*_spec.rb"
8
+ t.verbose = true
9
+ end
10
+
11
+ gemspec = Gem::Specification.load('convolver-light.gemspec')
12
+ Rake::ExtensionTask.new do |ext|
13
+ ext.name = 'convolver'
14
+ ext.source_pattern = "*.{c,h}"
15
+ ext.ext_dir = 'ext/convolver'
16
+ ext.lib_dir = 'lib/convolver'
17
+ ext.gem_spec = gemspec
18
+ end
19
+
20
+ task :default => [:compile, :test]
@@ -0,0 +1,30 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'convolver/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = 'convolver-light'
8
+ spec.version = Convolver::VERSION
9
+ spec.authors = ['Dima Ermilov']
10
+ spec.email = ['dima@scriptangle.com']
11
+ spec.description = 'Simplification of convolver gem, FFTW removed, suitable only for smaller kernels. Convolver gem author is Neil Slater, slobo777@gmail.com, https://github.com/neilslater'
12
+ spec.summary = 'Convolution for NArray simplified.'
13
+ spec.homepage = 'http://github.com/adworse/convolver-light'
14
+ spec.license = 'MIT'
15
+
16
+ spec.add_dependency "narray", ">= 0.6.0.8"
17
+
18
+ spec.add_development_dependency "yard", ">= 0.8.7.2"
19
+ spec.add_development_dependency "bundler", ">= 1.3"
20
+ spec.add_development_dependency "rspec", ">= 2.13.0"
21
+ spec.add_development_dependency "rake", ">= 1.9.1"
22
+ spec.add_development_dependency "rake-compiler", ">= 0.8.3"
23
+ spec.add_development_dependency "coveralls", ">= 0.6.7"
24
+
25
+ spec.files = `git ls-files`.split($/)
26
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
27
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
28
+ spec.extensions = spec.files.grep(%r{/extconf\.rb$})
29
+ spec.require_paths = ["lib"]
30
+ end
@@ -0,0 +1,107 @@
1
+ // ext/convolver/convolve_raw.c
2
+
3
+ #include "convolve_raw.h"
4
+
5
+ inline int size_from_shape( int rank, int *shape ) {
6
+ int size = 1;
7
+ int i;
8
+ for ( i = 0; i < rank; i++ ) { size *= shape[i]; }
9
+ return size;
10
+ }
11
+
12
+ // Sets reverse indices
13
+ inline void corner_reset( int rank, int *shape, int *rev_indices ) {
14
+ int i;
15
+ for ( i = 0; i < rank; i++ ) { rev_indices[i] = shape[i] - 1; }
16
+ return;
17
+ }
18
+
19
+ // Counts indices down, returns number of ranks that reset
20
+ inline int corner_dec( int rank, int *shape, int *rev_indices ) {
21
+ int i = 0;
22
+ while ( ! rev_indices[i]-- ) {
23
+ rev_indices[i] = shape[i] - 1;
24
+ i++;
25
+ }
26
+ return i;
27
+ }
28
+
29
+ // 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
+ int i, factor;
32
+ co_increment[0] = 1; // co-increment is always 1 in lowest rank
33
+ factor = 1;
34
+ for ( i = 0; i < rank; i++ ) {
35
+ co_increment[i+1] = co_increment[i] + factor * ( outer_shape[i] - inner_shape[i] );
36
+ factor *= outer_shape[i];
37
+ }
38
+ }
39
+
40
+ ////////////////////////////////////////////////////////////////////////////////////////////////////
41
+ //
42
+ // Convolve
43
+ //
44
+ // Benchmark: 640x480 image, 8x8 kernel, 1000 iterations. 12.3 seconds.
45
+ //
46
+
47
+ void convolve_raw(
48
+ int in_rank, int *in_shape, float *in_ptr,
49
+ int kernel_rank, int *kernel_shape, float *kernel_ptr,
50
+ int out_rank, int *out_shape, float *out_ptr ) {
51
+ int i, j, in_size, kernel_size, kernel_aligned, out_size, offset;
52
+ int out_co_incr[LARGEST_RANK], kernel_co_incr[LARGEST_RANK];
53
+ int ker_q[LARGEST_RANK], out_q[LARGEST_RANK];
54
+ int *kernel_co_incr_cache;
55
+
56
+ in_size = size_from_shape( in_rank, in_shape );
57
+ kernel_size = size_from_shape( kernel_rank, kernel_shape );
58
+ kernel_aligned = 4 * (kernel_size/4);
59
+ out_size = size_from_shape( out_rank, out_shape );
60
+
61
+ calc_co_increment( in_rank, in_shape, out_shape, out_co_incr );
62
+ calc_co_increment( in_rank, in_shape, kernel_shape, kernel_co_incr );
63
+
64
+ kernel_co_incr_cache = ALLOC_N( int, kernel_size );
65
+ kernel_co_incr_cache[0] = 0;
66
+
67
+ corner_reset( kernel_rank, kernel_shape, ker_q );
68
+ for ( i = 1; i < kernel_size; i++ ) {
69
+ kernel_co_incr_cache[i] = kernel_co_incr_cache[i-1] + kernel_co_incr[ corner_dec( kernel_rank, kernel_shape, ker_q ) ];
70
+ }
71
+
72
+ // For convenience of flow, we set offset to -1 and adjust countdown 1 higher to compensate
73
+ offset = -1;
74
+ corner_reset( out_rank, out_shape, out_q );
75
+ out_q[0]++;
76
+
77
+ // Main convolve loop
78
+ for ( i = 0; i < out_size; i++ ) {
79
+ __m128 simd_x, simd_y, simd_t;
80
+ float t = 0.0;
81
+ float v[4];
82
+ simd_t = _mm_setzero_ps();
83
+
84
+ offset += out_co_incr[ corner_dec( out_rank, out_shape, out_q ) ];
85
+
86
+ // Use SIMD for all the aligned values in groups of 4
87
+ for ( j = 0; j < kernel_aligned; j +=4 ) {
88
+ simd_x = _mm_load_ps( kernel_ptr + j );
89
+ // Yes the backwards alignment is correct
90
+ simd_y = _mm_set_ps( in_ptr[ offset + kernel_co_incr_cache[j+3] ], in_ptr[ offset + kernel_co_incr_cache[j+2] ],
91
+ in_ptr[ offset + kernel_co_incr_cache[j+1] ], in_ptr[ offset + kernel_co_incr_cache[j] ] );
92
+ simd_x = _mm_mul_ps( simd_x, simd_y );
93
+ simd_t = _mm_add_ps( simd_x, simd_t );
94
+ }
95
+ _mm_store_ps( v, simd_t );
96
+
97
+ // Complete any remaining 1,2 or 3 items one at a time
98
+ for ( j = kernel_aligned; j < kernel_size; j++ ) {
99
+ t += in_ptr[ offset + kernel_co_incr_cache[j] ] * kernel_ptr[ j ];
100
+ }
101
+
102
+ out_ptr[i] = v[0] + v[1] + v[2] + v[3] + t;
103
+ }
104
+
105
+ xfree( kernel_co_incr_cache );
106
+ return;
107
+ }
@@ -0,0 +1,22 @@
1
+ // ext/convolver/convolve_raw.h
2
+
3
+ ////////////////////////////////////////////////////////////////////////////////////////////////
4
+ //
5
+ // Declarations of narray helper functions
6
+ //
7
+
8
+ #ifndef CONVOLVE_RAW_H
9
+ #define CONVOLVE_RAW_H
10
+
11
+ #include <ruby.h>
12
+ #include <xmmintrin.h>
13
+ #include "narray_shared.h"
14
+
15
+ #define LARGEST_RANK 16
16
+
17
+ void convolve_raw(
18
+ int in_rank, int *in_shape, float *in_ptr,
19
+ int kernel_rank, int *kernel_shape, float *kernel_ptr,
20
+ int out_rank, int *out_shape, float *out_ptr );
21
+
22
+ #endif
@@ -0,0 +1,119 @@
1
+ // ext/convolver/convolver.c
2
+
3
+ #include <ruby.h>
4
+ #include "narray.h"
5
+ #include <stdio.h>
6
+ #include <xmmintrin.h>
7
+
8
+ #include "narray_shared.h"
9
+ #include "convolve_raw.h"
10
+
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
+ }
45
+
46
+ target_rank = na_a->rank;
47
+
48
+ for ( i = 0; i < target_rank; i++ ) {
49
+ if ( ( na_a->shape[i] - na_b->shape[i] ) < 0 ) {
50
+ rb_raise( rb_eArgError, "no space for backward fit" );
51
+ }
52
+ shift_by[i] = na_b->shape[i] >> 1;
53
+ }
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
+ }
63
+
64
+
65
+ /* @overload convolve_basic( signal, kernel )
66
+ * Calculates convolution of an array of floats representing a signal, with a second array representing
67
+ * a kernel. The two parameters must have the same rank. The output has same rank, its size in each dimension d is given by
68
+ * signal.shape[d] - kernel.shape[d] + 1
69
+ * @param [NArray] signal must be same size or larger than kernel in each dimension
70
+ * @param [NArray] kernel must be same size or smaller than signal in each dimension
71
+ * @return [NArray] result of convolving signal with kernel
72
+ */
73
+ static VALUE narray_convolve( VALUE self, VALUE a, VALUE b ) {
74
+ struct NARRAY *na_a, *na_b, *na_c;
75
+ volatile VALUE val_a, val_b, val_c;
76
+ int target_rank, i;
77
+ int target_shape[LARGEST_RANK];
78
+
79
+ val_a = na_cast_object(a, NA_SFLOAT);
80
+ GetNArray( val_a, na_a );
81
+
82
+ val_b = na_cast_object(b, NA_SFLOAT);
83
+ GetNArray( val_b, na_b );
84
+
85
+ if ( na_a->rank != na_b->rank ) {
86
+ rb_raise( rb_eArgError, "narray a must have equal rank to narray b (a rack %d, b rank %d)", na_a->rank, na_b->rank );
87
+ }
88
+
89
+ if ( na_a->rank > LARGEST_RANK ) {
90
+ rb_raise( rb_eArgError, "exceeded maximum narray rank for convolve of %d", LARGEST_RANK );
91
+ }
92
+
93
+ target_rank = na_a->rank;
94
+
95
+ for ( i = 0; i < target_rank; i++ ) {
96
+ target_shape[i] = na_a->shape[i] - na_b->shape[i] + 1;
97
+ if ( target_shape[i] < 1 ) {
98
+ rb_raise( rb_eArgError, "narray b is bigger in one or more dimensions than narray a" );
99
+ }
100
+ }
101
+
102
+ val_c = na_make_object( NA_SFLOAT, target_rank, target_shape, CLASS_OF( val_a ) );
103
+ GetNArray( val_c, na_c );
104
+
105
+ convolve_raw(
106
+ target_rank, na_a->shape, (float*) na_a->ptr,
107
+ target_rank, na_b->shape, (float*) na_b->ptr,
108
+ target_rank, target_shape, (float*) na_c->ptr );
109
+
110
+ return val_c;
111
+ }
112
+
113
+ void Init_convolver() {
114
+ Convolver = rb_define_module( "Convolver" );
115
+ rb_define_singleton_method( Convolver, "convolve_basic", narray_convolve, 2 );
116
+
117
+ // private method
118
+ rb_define_singleton_method( Convolver, "fit_kernel_backwards", narray_fit_backwards, 2 );
119
+ }
@@ -0,0 +1,27 @@
1
+ # ext/convolver/extconf.rb
2
+ require "mkmf"
3
+ require "rubygems"
4
+
5
+ # Following code stolen shamelessly from fftw3 gem:
6
+ narray_dir = File.dirname(Gem.find_files("narray.h").first) rescue $sitearchdir
7
+ dir_config('narray', narray_dir, narray_dir)
8
+
9
+ if ( ! ( have_header("narray.h") && have_header("narray_config.h") ) ) then
10
+ print <<-EOS
11
+ ** configure error **
12
+ Header narray.h or narray_config.h is not found. If you have these files in
13
+ /narraydir/include, try the following:
14
+
15
+ % ruby extconf.rb --with-narray-include=/narraydir/include
16
+
17
+ EOS
18
+ exit(-1)
19
+ end
20
+
21
+ # This also stolen from fftw3 gem (and not confirmed for Windows platforms - please let me know if it works!)
22
+ if /cygwin|mingw/ =~ RUBY_PLATFORM
23
+ have_library("narray") || raise("ERROR: narray library is not found")
24
+ end
25
+
26
+ $CFLAGS << ' -O3 -funroll-loops'
27
+ create_makefile( 'convolver/convolver' )
@@ -0,0 +1,61 @@
1
+ // ext/convolver/narray_shared.c
2
+
3
+ #include "narray_shared.h"
4
+
5
+ // This is copied from na_array.c, with safety checks and temp vars removed
6
+ int na_quick_idxs_to_pos( int rank, int *shape, int *idxs ) {
7
+ int i, pos = 0;
8
+ for ( i = rank - 1; i >= 0; i-- ) {
9
+ pos = pos * shape[i] + idxs[i];
10
+ }
11
+ return pos;
12
+ }
13
+
14
+ // This is inverse of above
15
+ void na_quick_pos_to_idxs( int rank, int *shape, int pos, int *idxs ) {
16
+ int i;
17
+ for ( i = 0; i < rank; i++ ) {
18
+ idxs[ i ] = pos % shape[i];
19
+ pos /= shape[i];
20
+ }
21
+ return;
22
+ }
23
+
24
+ // This is copied from na_array.c, with safety checks and temp vars removed
25
+ inline int na_inline_idxs_to_pos( int rank, int *shape, int *idxs ) {
26
+ int i, pos = 0;
27
+ for ( i = rank - 1; i >= 0; i-- ) {
28
+ pos = pos * shape[i] + idxs[i];
29
+ }
30
+ return pos;
31
+ }
32
+
33
+ // This is inverse of above
34
+ inline void na_inline_pos_to_idxs( int rank, int *shape, int pos, int *idxs ) {
35
+ int i;
36
+ for ( i = 0; i < rank; i++ ) {
37
+ idxs[ i ] = pos % shape[i];
38
+ pos /= shape[i];
39
+ }
40
+ return;
41
+ }
42
+
43
+ // used to place kernel data into array for FFTW3 processing
44
+ void fit_backwards_raw( int rank, int *dst_shape, float *dst, int *src_shape, float *src, int *shift_shape ) {
45
+ int i, j, size, x;
46
+ int k_idx[16], dst_idx[16];
47
+
48
+ size = 1;
49
+ for ( j = 0; j < rank; j++ ) { size *= src_shape[j]; }
50
+
51
+ for ( i = 0; i < size; i++ ) {
52
+ na_inline_pos_to_idxs( rank, src_shape, i, k_idx );
53
+ for ( j = 0; j < rank; j++ ) {
54
+ x = src_shape[j] - shift_shape[j] - k_idx[j] - 1;
55
+ if ( x < 0 ) x = x + dst_shape[j];
56
+ dst_idx[j] = x;
57
+ }
58
+ dst[ na_inline_idxs_to_pos( rank, dst_shape, dst_idx ) ] = src[i];
59
+ }
60
+ return;
61
+ }
@@ -0,0 +1,22 @@
1
+ // ext/convolver/narray_shared.h
2
+
3
+ ////////////////////////////////////////////////////////////////////////////////////////////////
4
+ //
5
+ // Declarations of narray helper functions
6
+ //
7
+
8
+ #ifndef CONVOLVER_NARRAY_SHARED_H
9
+ #define CONVOLVER_NARRAY_SHARED_H
10
+
11
+ #include <ruby.h>
12
+ #include "narray.h"
13
+
14
+ // This is copied from na_array.c, with safety checks and temp vars removed
15
+ int na_quick_idxs_to_pos( int rank, int *shape, int *idxs );
16
+
17
+ // This is inverse of above
18
+ void na_quick_pos_to_idxs( int rank, int *shape, int pos, int *idxs );
19
+
20
+ void fit_backwards_raw( int rank, int *dst_shape, float *dst, int *src_shape, float *src, int *shift_shape );
21
+
22
+ #endif
@@ -0,0 +1,15 @@
1
+ require 'narray'
2
+ require "convolver/convolver"
3
+ require "convolver/version"
4
+
5
+ module Convolver
6
+ # The two parameters must have the same rank. The output has same rank, its size in each
7
+ # dimension d is given by
8
+ # signal.shape[d] - kernel.shape[d] + 1
9
+ # @param [NArray] signal must be same size or larger than kernel in each dimension
10
+ # @param [NArray] kernel must be same size or smaller than signal in each dimension
11
+ # @return [NArray] result of convolving signal with kernel
12
+ def self.convolve(signal, kernel)
13
+ convolve_basic signal, kernel
14
+ end
15
+ end
@@ -0,0 +1,3 @@
1
+ module Convolver
2
+ VERSION = "0.3.1"
3
+ end
@@ -0,0 +1,84 @@
1
+ require 'helpers'
2
+
3
+ describe Convolver do
4
+ describe "#convolve_basic" do
5
+
6
+ it "should work like the example in the README" do
7
+ a = NArray[ 0.3, 0.4, 0.5 ]
8
+ b = NArray[ 1.3, -0.5 ]
9
+ c = Convolver.convolve_basic( a, b )
10
+ expect( c ).to be_narray_like NArray[ 0.19, 0.27 ]
11
+ end
12
+
13
+ it "should calculate a 2D convolution" do
14
+ a = NArray[ [ 0.3, 0.4, 0.5 ], [ 0.6, 0.8, 0.2 ], [ 0.9, 1.0, 0.1 ] ]
15
+ b = NArray[ [ 1.2, -0.5 ], [ 0.5, -1.3 ] ]
16
+ c = Convolver.convolve_basic( a, b )
17
+ expect( c ).to be_narray_like NArray[ [ -0.58, 0.37 ], [ -0.53, 1.23 ] ]
18
+ end
19
+
20
+ it "should calculate a 2D convolution with rectangular arrays" do
21
+ a = NArray[ [ 0.3, 0.4, 0.5, 0.3, 0.4 ], [ 0.6, 0.8, 0.2, 0.8, 0.2 ],
22
+ [ 0.9, 1.0, 0.1, 0.9, 1.0 ], [ 0.5, 0.9, 0.3, 0.2, 0.8 ], [ 0.7, 0.1, 0.3, 0.0, 0.1 ],
23
+ [ 0.4, 0.5, 0.6, 0.7, 0.8 ], [ 0.5, 0.4, 0.3, 0.2, 0.1 ] ]
24
+ b = NArray[ [ 1.2, -0.5, 0.2 ], [ 1.8, 0.5, -1.3 ] ]
25
+ c = Convolver.convolve_basic( a, b )
26
+ expect( c ).to be_narray_like NArray[ [ 1.48, 0.79, 1.03 ], [ 2.35, 1.7, -0.79 ], [ 1.56, 2.84, -0.53 ],
27
+ [ 1.13, 1.3, 0.83 ], [ 1.04, 0.26, 0.77 ], [ 1.06, 1.05, 1.04 ] ]
28
+ end
29
+
30
+ it "should calculate a 3D convolution" do
31
+ # 5x4x3
32
+ a = NArray[
33
+ [ [ 1.0, 0.6, 1.1, 0.2, 0.9 ], [ 1.0, 0.7, 0.8, 1.0, 1.0 ], [ 0.2, 0.6, 0.1, 0.2, 0.5 ], [ 0.5, 0.9, 0.2, 0.1, 0.6 ] ],
34
+ [ [ 0.4, 0.9, 0.4, 0.0, 0.6 ], [ 0.2, 1.1, 0.2, 0.4, 0.1 ], [ 0.4, 0.2, 0.5, 0.8, 0.7 ], [ 0.1, 0.9, 0.7, 0.1, 0.3 ] ],
35
+ [ [ 0.8, 0.6, 1.0, 0.1, 0.4 ], [ 0.3, 0.8, 0.6, 0.7, 1.1 ], [ 0.9, 1.0, 0.3, 0.4, 0.6 ], [ 0.2, 0.5, 0.4, 0.7, 0.2 ] ]
36
+ ]
37
+
38
+ # 3x3x3
39
+ b = NArray[
40
+ [ [ -0.9, 1.2, 0.8 ], [ 0.9, 0.1, -0.5 ], [ 1.1, 0.1, -1.1 ] ],
41
+ [ [ -0.2, -1.0, 1.4 ], [ -1.4, 0.0, 1.3 ], [ 0.3, 1.0, -0.5 ] ],
42
+ [ [ 0.6, 0.0, 0.7 ], [ -0.7, 1.1, 1.2 ], [ 1.3, 0.7, 0.0 ] ]
43
+ ]
44
+
45
+ # Should be 3x2x1
46
+ c = Convolver.convolve_basic( a, b )
47
+ expect( c ).to be_narray_like NArray[ [ [ 5.51, 3.04, 4.3 ], [ 3.04, 6.31, 3.87 ] ] ]
48
+ end
49
+
50
+ it "should calculate a 4D convolution" do
51
+ # 3x4x5x3
52
+ a = NArray[
53
+ [ [ [ 0.5, 0.4, 0.9 ], [ 0.1, 0.9, 0.8 ], [ 0.4, 0.0, 0.1 ], [ 0.8, 0.3, 0.4 ] ],
54
+ [ [ 0.0, 0.4, 0.0 ], [ 0.2, 0.3, 0.8 ], [ 0.6, 0.3, 0.2 ], [ 0.7, 0.4, 0.3 ] ],
55
+ [ [ 0.3, 0.3, 0.1 ], [ 0.6, 0.9, 0.4 ], [ 0.4, 0.0, 0.1 ], [ 0.8, 0.3, 0.4 ] ],
56
+ [ [ 0.0, 0.4, 0.0 ], [ 0.2, 0.3, 0.8 ], [ 0.6, 0.3, 0.2 ], [ 0.7, 0.4, 0.3 ] ],
57
+ [ [ 0.3, 0.3, 0.1 ], [ 0.6, 0.9, 0.4 ], [ 0.4, 0.0, 0.1 ], [ 0.8, 0.3, 0.4 ] ] ],
58
+ [ [ [ 0.5, 0.4, 0.9 ], [ 0.1, 0.9, 0.8 ], [ 0.4, 0.0, 0.1 ], [ 0.8, 0.3, 0.4 ] ],
59
+ [ [ 0.0, 0.4, 0.0 ], [ 0.2, 0.3, 0.8 ], [ 0.6, 0.3, 0.2 ], [ 0.7, 0.4, 0.3 ] ],
60
+ [ [ 0.3, 0.3, 0.1 ], [ 0.6, 0.9, 0.4 ], [ 0.4, 0.0, 0.1 ], [ 0.8, 0.3, 0.4 ] ],
61
+ [ [ 0.0, 0.4, 0.0 ], [ 0.2, 0.3, 0.8 ], [ 0.6, 0.3, 0.2 ], [ 0.7, 0.4, 0.3 ] ],
62
+ [ [ 0.3, 0.3, 0.1 ], [ 0.6, 0.9, 0.4 ], [ 0.4, 0.0, 0.1 ], [ 0.8, 0.3, 0.4 ] ] ],
63
+ [ [ [ 0.5, 0.4, 0.9 ], [ 0.1, 0.9, 0.8 ], [ 0.4, 0.0, 0.1 ], [ 0.8, 0.3, 0.4 ] ],
64
+ [ [ 0.0, 0.4, 0.0 ], [ 0.2, 0.3, 0.8 ], [ 0.6, 0.3, 0.2 ], [ 0.7, 0.4, 0.3 ] ],
65
+ [ [ 0.3, 0.3, 0.1 ], [ 0.6, 0.9, 0.4 ], [ 0.4, 0.0, 0.1 ], [ 0.8, 0.3, 0.4 ] ],
66
+ [ [ 0.0, 0.4, 0.0 ], [ 0.2, 0.3, 0.8 ], [ 0.6, 0.3, 0.2 ], [ 0.7, 0.4, 0.3 ] ],
67
+ [ [ 0.3, 0.3, 0.1 ], [ 0.6, 0.9, 0.4 ], [ 0.4, 0.0, 0.1 ], [ 0.8, 0.3, 0.4 ] ] ] ]
68
+
69
+ # 2x3x3x2
70
+ b = NArray[ [
71
+ [ [ 1.1, 0.6 ], [ 1.2, 0.6 ], [ 0.8, 0.1 ] ], [ [ -0.4, 0.8 ], [ 0.5, 0.4 ], [ 1.2, 0.2 ] ],
72
+ [ [ 0.8, 0.2 ], [ 0.5, 0.0 ], [ 1.4, 1.3 ] ] ],
73
+ [ [ [ 1.1, 0.6 ], [ 1.2, 0.6 ], [ 0.8, 0.1 ] ], [ [ -0.4, 0.8 ], [ 0.5, 0.4 ], [ 1.2, 0.2 ] ],
74
+ [ [ 0.8, 0.2 ], [ 0.5, 0.0 ], [ 1.4, 1.3 ] ] ] ]
75
+
76
+ # Should be 2x2x3x2
77
+ c = Convolver.convolve_basic( a, b )
78
+ expect( c ).to be_narray_like NArray[
79
+ [ [ [ 8.5, 8.2 ], [ 11.34, 9.68 ] ], [ [ 7.68, 6.56 ], [ 11.24, 7.16 ] ], [ [ 9.14, 6.54 ], [ 12.44, 9.2 ] ] ],
80
+ [ [ [ 8.5, 8.2 ], [ 11.34, 9.68 ] ], [ [ 7.68, 6.56 ], [ 11.24, 7.16 ] ], [ [ 9.14, 6.54 ], [ 12.44, 9.2 ] ] ]
81
+ ]
82
+ end
83
+ end
84
+ end
@@ -0,0 +1,49 @@
1
+ require 'helpers'
2
+
3
+ describe Convolver do
4
+ describe "#convolve" do
5
+
6
+ it "should work like the example in the README" do
7
+ a = NArray[ 0.3, 0.4, 0.5 ]
8
+ b = NArray[ 1.3, -0.5 ]
9
+ c = Convolver.convolve( a, b )
10
+ expect( c ).to be_narray_like NArray[ 0.19, 0.27 ]
11
+ end
12
+
13
+ it "should process convolutions of different sizes" do
14
+ # The variety here is to ensure all branches of optimisation algorithm
15
+ # are covered
16
+ [10,30,60,90,100,120,130,150,175,200].each do |asize|
17
+ [5,10,12,15,20,30,40,50].each do |bsize|
18
+ next unless bsize < asize
19
+ a = NArray.sfloat(asize,asize).random()
20
+ b = NArray.sfloat(bsize,bsize).random()
21
+ c = Convolver.convolve( a, b )
22
+
23
+ # We should always match output of convolve_basic irrespective
24
+ # of what the optimal choice of algorithm is (larger error allowed here due to rounding)
25
+ expect_result = Convolver.convolve_basic( a, b )
26
+ expect( c ).to be_narray_like( expect_result, 1e-6 )
27
+ end
28
+ end
29
+ end
30
+
31
+ it "should choose #convolve_basic for small inputs" do
32
+ a = NArray.sfloat(50,50).random()
33
+ b = NArray.sfloat(10,10).random()
34
+ expect(Convolver).to receive(:convolve_basic).once
35
+ expect(Convolver).to_not receive(:convolve_fftw3)
36
+ c = Convolver.convolve( a, b )
37
+ end
38
+
39
+ it "should choose #convolve_fftw3 for large inputs" do
40
+ a = NArray.sfloat(500,500).random()
41
+ b = NArray.sfloat(100,100).random()
42
+ expect(Convolver).to receive(:convolve_fftw3).once
43
+ expect(Convolver).to_not receive(:convolve_basic)
44
+ c = Convolver.convolve( a, b )
45
+ end
46
+
47
+ end
48
+
49
+ end
@@ -0,0 +1,46 @@
1
+ # convolver/spec/helpers.rb
2
+ require 'coveralls'
3
+
4
+ Coveralls.wear!
5
+
6
+ require 'convolver'
7
+
8
+ # Matcher compares NArrays numerically
9
+ RSpec::Matchers.define :be_narray_like do |expected_narray, mse = 1e-9|
10
+ match do |given|
11
+ @error = nil
12
+ if ! given.is_a?(NArray)
13
+ @error = "Wrong class."
14
+ elsif given.shape != expected_narray.shape
15
+ @error = "Shapes are different."
16
+ else
17
+ d = given - expected_narray
18
+ difference = ( d * d ).sum / d.size
19
+ if difference > mse
20
+ @error = "Numerical difference with mean square error #{difference}"
21
+ end
22
+ end
23
+ @given = given.clone
24
+
25
+ if @error
26
+ @expected = expected_narray.clone
27
+ end
28
+
29
+ ! @error
30
+ end
31
+
32
+ failure_message do
33
+ "NArray does not match supplied example. #{@error}
34
+ Expected: #{@expected.inspect}
35
+ Got: #{@given.inspect}"
36
+ end
37
+
38
+ failure_message_when_negated do
39
+ "NArray is too close to unwanted example.
40
+ Unwanted: #{@given.inspect}"
41
+ end
42
+
43
+ description do |given, expected|
44
+ "numerically very close to example"
45
+ end
46
+ end
metadata ADDED
@@ -0,0 +1,165 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: convolver-light
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.3.1
5
+ platform: ruby
6
+ authors:
7
+ - Dima Ermilov
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2018-11-01 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: narray
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: 0.6.0.8
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: 0.6.0.8
27
+ - !ruby/object:Gem::Dependency
28
+ name: yard
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: 0.8.7.2
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: 0.8.7.2
41
+ - !ruby/object:Gem::Dependency
42
+ name: bundler
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '1.3'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '1.3'
55
+ - !ruby/object:Gem::Dependency
56
+ name: rspec
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: 2.13.0
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: 2.13.0
69
+ - !ruby/object:Gem::Dependency
70
+ name: rake
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: 1.9.1
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: 1.9.1
83
+ - !ruby/object:Gem::Dependency
84
+ name: rake-compiler
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ">="
88
+ - !ruby/object:Gem::Version
89
+ version: 0.8.3
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ">="
95
+ - !ruby/object:Gem::Version
96
+ version: 0.8.3
97
+ - !ruby/object:Gem::Dependency
98
+ name: coveralls
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - ">="
102
+ - !ruby/object:Gem::Version
103
+ version: 0.6.7
104
+ type: :development
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - ">="
109
+ - !ruby/object:Gem::Version
110
+ version: 0.6.7
111
+ description: Simplification of convolver gem, FFTW removed, suitable only for smaller
112
+ kernels. Convolver gem author is Neil Slater, slobo777@gmail.com, https://github.com/neilslater
113
+ email:
114
+ - dima@scriptangle.com
115
+ executables: []
116
+ extensions:
117
+ - ext/convolver/extconf.rb
118
+ extra_rdoc_files: []
119
+ files:
120
+ - ".gitignore"
121
+ - ".travis.yml"
122
+ - Gemfile
123
+ - LICENSE.txt
124
+ - README.md
125
+ - Rakefile
126
+ - convolver-light.gemspec
127
+ - ext/convolver/convolve_raw.c
128
+ - ext/convolver/convolve_raw.h
129
+ - ext/convolver/convolver.c
130
+ - ext/convolver/extconf.rb
131
+ - ext/convolver/narray_shared.c
132
+ - ext/convolver/narray_shared.h
133
+ - lib/convolver-light.rb
134
+ - lib/convolver/version.rb
135
+ - spec/convolve_basic_spec.rb
136
+ - spec/convolve_spec.rb
137
+ - spec/helpers.rb
138
+ homepage: http://github.com/adworse/convolver-light
139
+ licenses:
140
+ - MIT
141
+ metadata: {}
142
+ post_install_message:
143
+ rdoc_options: []
144
+ require_paths:
145
+ - lib
146
+ required_ruby_version: !ruby/object:Gem::Requirement
147
+ requirements:
148
+ - - ">="
149
+ - !ruby/object:Gem::Version
150
+ version: '0'
151
+ required_rubygems_version: !ruby/object:Gem::Requirement
152
+ requirements:
153
+ - - ">="
154
+ - !ruby/object:Gem::Version
155
+ version: '0'
156
+ requirements: []
157
+ rubyforge_project:
158
+ rubygems_version: 2.7.6
159
+ signing_key:
160
+ specification_version: 4
161
+ summary: Convolution for NArray simplified.
162
+ test_files:
163
+ - spec/convolve_basic_spec.rb
164
+ - spec/convolve_spec.rb
165
+ - spec/helpers.rb