convolver 0.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.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 254fbf54caf7407108357aee612ab3c08af4ea4c
4
+ data.tar.gz: f4212255722174976e92c734b76ce956365ad936
5
+ SHA512:
6
+ metadata.gz: 55d4082e492a48d09316cf4f6c0f44ad1dd5bae756fd9ef3d8890c592ed0c863029678eded0a3783559e7acfdb8ca9198ce3d4938c74174220f5452f35524d0d
7
+ data.tar.gz: 3a9ee78cb91ca935c83f0b56e93d58799871e97d5f8174ff9ba4e86ec798469dad6f3167482458c24f37bdf47aed4b6b5f5aaf6e658f9329e63aab16daa5743d
data/.gitignore ADDED
@@ -0,0 +1,18 @@
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
data/.travis.yml ADDED
@@ -0,0 +1,8 @@
1
+ language: ruby
2
+ rvm:
3
+ - "1.8.7"
4
+ - "1.9.3"
5
+ - "2.0.0"
6
+ - rbx-18mode
7
+ - rbx-19mode
8
+ - ree
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in convolver.gemspec
4
+ gemspec
data/LICENSE.txt ADDED
@@ -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.
data/README.md ADDED
@@ -0,0 +1,46 @@
1
+ # Convolver
2
+
3
+ [![Build Status](https://travis-ci.org/neilslater/convolver.png?branch=master)](http://travis-ci.org/neilslater/convolver)
4
+
5
+ Adds an "inner" convolve operation to NArray floats. It is around 250 times faster than equivalents
6
+ in pure Ruby.
7
+
8
+ Note that convolves based on FFTW3 could well be faster still for large arrays with large kernels.
9
+
10
+ ## Installation
11
+
12
+ Add this line to your application's Gemfile:
13
+
14
+ gem 'convolver'
15
+
16
+ And then execute:
17
+
18
+ $ bundle
19
+
20
+ Or install it yourself as:
21
+
22
+ $ gem install convolver
23
+
24
+ ## Usage
25
+
26
+ Basic convolution:
27
+
28
+ a = NArray[0.3,0.4,0.5]
29
+ b = NArray[1.3, -0.5]
30
+ c = Convolver.convolve( a, b )
31
+ => NArray.float(2): [ 0.19, 0.27 ]
32
+
33
+ * Convolver only works on single-precision floats internally. It will cast NArray types to this, if
34
+ possible, prior to calculating.
35
+ * The convolution is an "inner" one. The output is smaller than the input, each dimension is reduced
36
+ by 1 less than the width of the kernel in the same dimension.
37
+ * Convolver expects input a and kernel b to have the same rank, and for the kernel to be same size
38
+ or smaller in all dimensions as the input.
39
+
40
+ ## Contributing
41
+
42
+ 1. Fork it
43
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
44
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
45
+ 4. Push to the branch (`git push origin my-new-feature`)
46
+ 5. Create new Pull Request
data/Rakefile ADDED
@@ -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.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,17 @@
1
+ require 'convolver'
2
+ require 'narray'
3
+ require 'benchmark'
4
+
5
+ class Convolver2DBenchmark
6
+ attr_reader :image, :kernel
7
+
8
+ def initialize
9
+ @image = NArray.float(640, 480).random
10
+ @kernel = NArray.float(8, 8).random
11
+ end
12
+ end
13
+
14
+ Benchmark.bm do |x|
15
+ source = Convolver2DBenchmark.new
16
+ x.report('kilo') { 1000.times { Convolver.convolve( source.image, source.kernel ) } }
17
+ end
data/convolver.gemspec ADDED
@@ -0,0 +1,29 @@
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"
8
+ spec.version = Convolver::VERSION
9
+ spec.authors = ["Neil Slater"]
10
+ spec.email = ["slobo777@gmail.com"]
11
+ spec.description = %q{Convolution for NArray}
12
+ spec.summary = %q{Convolution for NArray}
13
+ spec.homepage = "http://github.com/neilslater/convolver"
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
+
24
+ spec.files = `git ls-files`.split($/)
25
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
26
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
27
+ spec.extensions = spec.files.grep(%r{/extconf\.rb$})
28
+ spec.require_paths = ["lib"]
29
+ end
@@ -0,0 +1,184 @@
1
+ // ext/convolver/convolver.c
2
+
3
+ #include <ruby.h>
4
+ #include "narray.h"
5
+ #include <stdio.h>
6
+ #include <xmmintrin.h>
7
+
8
+ #define LARGEST_RANK 16
9
+
10
+ // This is copied from na_array.c, with safety checks and temp vars removed
11
+ inline int na_quick_idxs_to_pos( int rank, int *shape, int *idxs ) {
12
+ int i, pos = 0;
13
+ for ( i = rank - 1; i >= 0; i-- ) {
14
+ pos = pos * shape[i] + idxs[i];
15
+ }
16
+ return pos;
17
+ }
18
+
19
+ // This is inverse of above
20
+ inline void na_quick_pos_to_idxs( int rank, int *shape, int pos, int *idxs ) {
21
+ int i;
22
+ for ( i = 0; i < rank; i++ ) {
23
+ idxs[ i ] = pos % shape[i];
24
+ pos /= shape[i];
25
+ }
26
+ return;
27
+ }
28
+
29
+ inline int size_from_shape( int rank, int *shape ) {
30
+ int size = 1;
31
+ int i;
32
+ for ( i = 0; i < rank; i++ ) { size *= shape[i]; }
33
+ return size;
34
+ }
35
+
36
+ // Sets reverse indices
37
+ inline void corner_reset( int rank, int *shape, int *rev_indices ) {
38
+ int i;
39
+ for ( i = 0; i < rank; i++ ) { rev_indices[i] = shape[i] - 1; }
40
+ return;
41
+ }
42
+
43
+ // Counts indices down, returns number of ranks that reset
44
+ inline int corner_dec( int rank, int *shape, int *rev_indices ) {
45
+ int i = 0;
46
+ while ( ! rev_indices[i]-- ) {
47
+ rev_indices[i] = shape[i] - 1;
48
+ i++;
49
+ }
50
+ return i;
51
+ }
52
+
53
+ // Generates co-increment steps by rank boundaries crossed, for the outer position as inner position is incremented by 1
54
+ inline void calc_co_increment( int rank, int *outer_shape, int *inner_shape, int *co_increment ) {
55
+ int i, factor;
56
+ co_increment[0] = 1; // co-increment is always 1 in lowest rank
57
+ factor = 1;
58
+ for ( i = 0; i < rank; i++ ) {
59
+ co_increment[i+1] = co_increment[i] + factor * ( outer_shape[i] - inner_shape[i] );
60
+ factor *= outer_shape[i];
61
+ }
62
+ }
63
+
64
+ ////////////////////////////////////////////////////////////////////////////////////////////////////
65
+ //
66
+ // Convolve method 4.
67
+ //
68
+ // Benchmark: 640x480 image, 8x8 kernel, 1000 iterations. 11.54 seconds. Score: 63
69
+ //
70
+
71
+ void convolve_raw(
72
+ int in_rank, int *in_shape, float *in_ptr,
73
+ int kernel_rank, int *kernel_shape, float *kernel_ptr,
74
+ int out_rank, int *out_shape, float *out_ptr ) {
75
+ int i, j, in_size, kernel_size, kernel_aligned, out_size, offset;
76
+ int out_co_incr[LARGEST_RANK], kernel_co_incr[LARGEST_RANK];
77
+ int ker_q[LARGEST_RANK], out_q[LARGEST_RANK];
78
+ int *kernel_co_incr_cache;
79
+
80
+ in_size = size_from_shape( in_rank, in_shape );
81
+ kernel_size = size_from_shape( kernel_rank, kernel_shape );
82
+ kernel_aligned = 4 * (kernel_size/4);
83
+ out_size = size_from_shape( out_rank, out_shape );
84
+
85
+ calc_co_increment( in_rank, in_shape, out_shape, out_co_incr );
86
+ calc_co_increment( in_rank, in_shape, kernel_shape, kernel_co_incr );
87
+
88
+ kernel_co_incr_cache = ALLOC_N( int, kernel_size );
89
+ kernel_co_incr_cache[0] = 0;
90
+
91
+ corner_reset( kernel_rank, kernel_shape, ker_q );
92
+ for ( i = 1; i < kernel_size; i++ ) {
93
+ kernel_co_incr_cache[i] = kernel_co_incr_cache[i-1] + kernel_co_incr[ corner_dec( kernel_rank, kernel_shape, ker_q ) ];
94
+ }
95
+
96
+ // For convenience of flow, we set offset to -1 and adjust countdown 1 higher to compensate
97
+ offset = -1;
98
+ corner_reset( out_rank, out_shape, out_q );
99
+ out_q[0]++;
100
+
101
+ // Main convolve loop
102
+ for ( i = 0; i < out_size; i++ ) {
103
+ __m128 simd_x, simd_y, simd_t;
104
+ float t = 0.0;
105
+ simd_t = _mm_setzero_ps();
106
+
107
+ offset += out_co_incr[ corner_dec( out_rank, out_shape, out_q ) ];
108
+
109
+ // Use SIMD for all the aligned values in groups of 4
110
+ for ( j = 0; j < kernel_aligned; j +=4 ) {
111
+ simd_x = _mm_load_ps( kernel_ptr + j );
112
+ // Yes the backwards alignment is correct
113
+ simd_y = _mm_set_ps( in_ptr[ offset + kernel_co_incr_cache[j+3] ], in_ptr[ offset + kernel_co_incr_cache[j+2] ],
114
+ in_ptr[ offset + kernel_co_incr_cache[j+1] ], in_ptr[ offset + kernel_co_incr_cache[j] ] );
115
+ simd_x = _mm_mul_ps( simd_x, simd_y );
116
+ simd_t = _mm_add_ps( simd_x, simd_t );
117
+ }
118
+
119
+ // Complete any remaining 1,2 or 3 items one at a time
120
+ for ( j = kernel_aligned; j < kernel_size; j++ ) {
121
+ t += in_ptr[ offset + kernel_co_incr_cache[j] ] * kernel_ptr[ j ];
122
+ }
123
+
124
+ out_ptr[i] = simd_t[0] + simd_t[1] + simd_t[2] + simd_t[3] + t;
125
+ }
126
+
127
+ xfree( kernel_co_incr_cache );
128
+ return;
129
+ }
130
+
131
+
132
+ ////////////////////////////////////////////////////////////////////////////////////////////////////
133
+
134
+ // To hold the module object
135
+ VALUE Convolver = Qnil;
136
+
137
+ static VALUE narray_convolve( VALUE self, VALUE a, VALUE b ) {
138
+ struct NARRAY *na_a, *na_b, *na_c;
139
+ volatile VALUE val_a, val_b, val_c;
140
+ int target_rank, i;
141
+ int target_shape[LARGEST_RANK];
142
+
143
+ val_a = na_cast_object(a, NA_SFLOAT);
144
+ GetNArray( val_a, na_a );
145
+
146
+ val_b = na_cast_object(b, NA_SFLOAT);
147
+ GetNArray( val_b, na_b );
148
+
149
+ if ( na_a->rank < na_b->rank ) {
150
+ rb_raise( rb_eArgError, "narray b must have equal or lower rank than narray a" );
151
+ }
152
+
153
+ if ( na_a->rank < na_b->rank ) {
154
+ rb_raise( rb_eArgError, "narray a must have equal rank to narray b (temporary restriction)" );
155
+ }
156
+
157
+ if ( na_a->rank > LARGEST_RANK ) {
158
+ rb_raise( rb_eArgError, "exceeded maximum narray rank for convolve of %d", LARGEST_RANK );
159
+ }
160
+
161
+ target_rank = na_a->rank;
162
+
163
+ for ( i = 0; i < target_rank; i++ ) {
164
+ target_shape[i] = na_a->shape[i] - na_b->shape[i] + 1;
165
+ if ( target_shape[i] < 1 ) {
166
+ rb_raise( rb_eArgError, "narray b is bigger in one or more dimensions than narray a" );
167
+ }
168
+ }
169
+
170
+ val_c = na_make_object( NA_SFLOAT, target_rank, target_shape, CLASS_OF( val_a ) );
171
+ GetNArray( val_c, na_c );
172
+
173
+ convolve_raw(
174
+ target_rank, na_a->shape, (float*) na_a->ptr,
175
+ target_rank, na_b->shape, (float*) na_b->ptr,
176
+ target_rank, target_shape, (float*) na_c->ptr );
177
+
178
+ return val_c;
179
+ }
180
+
181
+ void Init_convolver() {
182
+ Convolver = rb_define_module( "Convolver" );
183
+ rb_define_singleton_method( Convolver, "convolve", narray_convolve, 2 );
184
+ }
@@ -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' )
data/lib/convolver.rb ADDED
@@ -0,0 +1,11 @@
1
+ require 'narray'
2
+ require "convolver/convolver"
3
+ require "convolver/version"
4
+
5
+ module Convolver
6
+ # Calculates float convolution of an array with a kernel
7
+ # @param [NArray] a outer array
8
+ # @param [NArray] b kernel
9
+ # @return [NArray] result of convolving a with b
10
+ # @!parse def self.convolve(a,b); end
11
+ end
@@ -0,0 +1,3 @@
1
+ module Convolver
2
+ VERSION = "0.0.1"
3
+ end
@@ -0,0 +1,84 @@
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
+ c.should 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( a, b )
17
+ c.should 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( a, b )
26
+ c.should 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( a, b )
47
+ c.should 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( a, b )
78
+ c.should 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
data/spec/helpers.rb ADDED
@@ -0,0 +1,42 @@
1
+ # convolver/spec/helpers.rb
2
+ require 'convolver'
3
+
4
+ # Matcher compares NArrays numerically
5
+ RSpec::Matchers.define :be_narray_like do |expected_narray|
6
+ match do |given|
7
+ @error = nil
8
+ if ! given.is_a?(NArray)
9
+ @error = "Wrong class."
10
+ elsif given.shape != expected_narray.shape
11
+ @error = "Shapes are different."
12
+ else
13
+ d = given - expected_narray
14
+ difference = ( d * d ).sum / d.size
15
+ if difference > 1e-10
16
+ @error = "Numerical difference with mean square error #{difference}"
17
+ end
18
+ end
19
+ @given = given.clone
20
+
21
+ if @error
22
+ @expected = expected_narray.clone
23
+ end
24
+
25
+ ! @error
26
+ end
27
+
28
+ failure_message_for_should do
29
+ "NArray does not match supplied example. #{@error}
30
+ Expected: #{@expected.inspect}
31
+ Got: #{@given.inspect}"
32
+ end
33
+
34
+ failure_message_for_should_not do
35
+ "NArray is too close to unwanted example.
36
+ Unwanted: #{@given.inspect}"
37
+ end
38
+
39
+ description do |given, expected|
40
+ "numerically very close to example"
41
+ end
42
+ end
metadata ADDED
@@ -0,0 +1,146 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: convolver
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Neil Slater
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2013-10-20 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
+ description: Convolution for NArray
98
+ email:
99
+ - slobo777@gmail.com
100
+ executables: []
101
+ extensions:
102
+ - ext/convolver/extconf.rb
103
+ extra_rdoc_files: []
104
+ files:
105
+ - .gitignore
106
+ - .travis.yml
107
+ - Gemfile
108
+ - LICENSE.txt
109
+ - README.md
110
+ - Rakefile
111
+ - benchmarks/convolve_benchmark.rb
112
+ - convolver.gemspec
113
+ - ext/convolver/convolver.c
114
+ - ext/convolver/extconf.rb
115
+ - lib/convolver.rb
116
+ - lib/convolver/version.rb
117
+ - spec/convolver_spec.rb
118
+ - spec/helpers.rb
119
+ homepage: http://github.com/neilslater/convolver
120
+ licenses:
121
+ - MIT
122
+ metadata: {}
123
+ post_install_message:
124
+ rdoc_options: []
125
+ require_paths:
126
+ - lib
127
+ required_ruby_version: !ruby/object:Gem::Requirement
128
+ requirements:
129
+ - - '>='
130
+ - !ruby/object:Gem::Version
131
+ version: '0'
132
+ required_rubygems_version: !ruby/object:Gem::Requirement
133
+ requirements:
134
+ - - '>='
135
+ - !ruby/object:Gem::Version
136
+ version: '0'
137
+ requirements: []
138
+ rubyforge_project:
139
+ rubygems_version: 2.1.8
140
+ signing_key:
141
+ specification_version: 4
142
+ summary: Convolution for NArray
143
+ test_files:
144
+ - spec/convolver_spec.rb
145
+ - spec/helpers.rb
146
+ has_rdoc: