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 +7 -0
- data/.gitignore +18 -0
- data/.travis.yml +8 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +46 -0
- data/Rakefile +20 -0
- data/benchmarks/convolve_benchmark.rb +17 -0
- data/convolver.gemspec +29 -0
- data/ext/convolver/convolver.c +184 -0
- data/ext/convolver/extconf.rb +27 -0
- data/lib/convolver.rb +11 -0
- data/lib/convolver/version.rb +3 -0
- data/spec/convolver_spec.rb +84 -0
- data/spec/helpers.rb +42 -0
- metadata +146 -0
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
data/.travis.yml
ADDED
data/Gemfile
ADDED
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
|
+
[](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,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:
|