convolver-light 0.3.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -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