convolver 0.4.0 → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +47 -0
- data/README.md +68 -55
- data/ext/convolver/convolve_raw.c +6 -6
- data/ext/convolver/convolve_raw.h +0 -2
- data/ext/convolver/convolver.c +63 -92
- data/ext/convolver/extconf.rb +37 -24
- data/lib/convolver/version.rb +2 -1
- data/lib/convolver.rb +92 -83
- metadata +26 -23
- data/.github/workflows/ci.yml +0 -40
- data/.gitignore +0 -19
- data/.rubocop.yml +0 -16
- data/Gemfile +0 -15
- data/Rakefile +0 -22
- data/convolver.gemspec +0 -27
- data/ext/convolver/narray_shared.c +0 -61
- data/ext/convolver/narray_shared.h +0 -22
- data/spec/convolver_basic_spec.rb +0 -89
- data/spec/convolver_fftw3_spec.rb +0 -166
- data/spec/convolver_spec.rb +0 -53
- data/spec/helpers.rb +0 -43
data/lib/convolver.rb
CHANGED
|
@@ -1,100 +1,109 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require 'narray'
|
|
3
|
+
require 'numo/narray/alt'
|
|
4
|
+
require 'numo/pocketfft'
|
|
4
5
|
require 'convolver/convolver'
|
|
5
6
|
require 'convolver/version'
|
|
6
|
-
require 'fftw3'
|
|
7
7
|
|
|
8
|
-
#
|
|
8
|
+
# Valid cross-correlation operations for Numo::NArray values.
|
|
9
9
|
module Convolver
|
|
10
|
-
#
|
|
11
|
-
|
|
12
|
-
# dimension d is given by
|
|
13
|
-
# signal.shape[d] - kernel.shape[d] + 1
|
|
14
|
-
# If you always perform convolutions of the same size, you may be better off benchmarking your
|
|
15
|
-
# own code using either #convolve_basic or #convolve_fftw3, and have your code use the fastest.
|
|
16
|
-
# @param [NArray] signal must be same size or larger than kernel in each dimension
|
|
17
|
-
# @param [NArray] kernel must be same size or smaller than signal in each dimension
|
|
18
|
-
# @return [NArray] result of convolving signal with kernel
|
|
19
|
-
def self.convolve(signal, kernel)
|
|
20
|
-
# For small signals or kernels, just go straight to basic
|
|
21
|
-
return convolve_basic(signal, kernel) if signal.size < 1000 || kernel.size < 100
|
|
22
|
-
|
|
23
|
-
# If predicted time is less than a millisecond, just do a basic convolve
|
|
24
|
-
basic_time_predicted = predict_convolve_basic_time(signal, kernel)
|
|
25
|
-
return convolve_basic(signal, kernel) if basic_time_predicted < 0.1
|
|
26
|
-
|
|
27
|
-
# Factor of two to allow for large uncertainty in predictions for FFTW3
|
|
28
|
-
fft_time_predicted = predict_convolve_fft_time(signal, kernel)
|
|
29
|
-
return convolve_fftw3(signal, kernel) if fft_time_predicted < 2 * basic_time_predicted
|
|
30
|
-
|
|
31
|
-
convolve_basic(signal, kernel)
|
|
32
|
-
end
|
|
10
|
+
# Maximum number of dimensions supported by the direct native implementation.
|
|
11
|
+
MAX_RANK = 16
|
|
33
12
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
13
|
+
class << self
|
|
14
|
+
# Chooses the likely fastest implementation for a valid cross-correlation.
|
|
15
|
+
#
|
|
16
|
+
# The inputs must have the same rank, and the kernel must not be larger than
|
|
17
|
+
# the signal in any dimension. The result shape is:
|
|
18
|
+
#
|
|
19
|
+
# signal.shape.zip(kernel.shape).map { |signal_size, kernel_size| signal_size - kernel_size + 1 }
|
|
20
|
+
#
|
|
21
|
+
# @param signal [Numo::NArray] input values
|
|
22
|
+
# @param kernel [Numo::NArray] correlation kernel
|
|
23
|
+
# @return [Numo::SFloat] valid cross-correlation result
|
|
24
|
+
# @raise [ArgumentError] if the inputs have incompatible ranks or shapes
|
|
25
|
+
def convolve(signal, kernel)
|
|
26
|
+
validate_inputs!(signal, kernel)
|
|
27
|
+
return convolve_basic(signal, kernel) if signal.size < 1000 || kernel.size < 100
|
|
48
28
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
# varying between 1 and 12 milliseconds on the test computer.
|
|
52
|
-
# @param [NArray] signal must be same size or larger than kernel in each dimension
|
|
53
|
-
# @param [NArray] kernel must be same size or smaller than signal in each dimension
|
|
54
|
-
# @return [Float] rough estimate of time for convolution compared to baseline
|
|
55
|
-
def self.predict_convolve_fft_time(signal, kernel)
|
|
56
|
-
16 * 4.55e-08 * result_shape(signal.shape, kernel.shape).inject(1) { |t, x| t * x * Math.log(x) }
|
|
57
|
-
end
|
|
29
|
+
basic_time_predicted = predict_convolve_basic_time(signal, kernel)
|
|
30
|
+
return convolve_basic(signal, kernel) if basic_time_predicted < 0.1
|
|
58
31
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
# varying bewteen 2 and 8 milliseconds on the test computer.
|
|
62
|
-
# @param [NArray] signal must be same size or larger than kernel in each dimension
|
|
63
|
-
# @param [NArray] kernel must be same size or smaller than signal in each dimension
|
|
64
|
-
# @return [Float] rough estimate of time for convolution compared to baseline
|
|
65
|
-
def self.predict_convolve_basic_time(signal, kernel)
|
|
66
|
-
outputs = shape_to_size(result_shape(signal.shape, kernel.shape))
|
|
67
|
-
4.54e-12 * (outputs * shape_to_size(signal.shape) * shape_to_size(kernel.shape))
|
|
68
|
-
end
|
|
32
|
+
fft_time_predicted = predict_convolve_fft_time(signal, kernel)
|
|
33
|
+
return convolve_fft(signal, kernel) if fft_time_predicted < 2 * basic_time_predicted
|
|
69
34
|
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
end
|
|
35
|
+
convolve_basic(signal, kernel)
|
|
36
|
+
end
|
|
73
37
|
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
38
|
+
# Uses PocketFFT to calculate a valid cross-correlation.
|
|
39
|
+
#
|
|
40
|
+
# @param signal [Numo::NArray] input values
|
|
41
|
+
# @param kernel [Numo::NArray] correlation kernel
|
|
42
|
+
# @return [Numo::SFloat] valid cross-correlation result
|
|
43
|
+
# @raise [ArgumentError] if the inputs have incompatible ranks or shapes
|
|
44
|
+
def convolve_fft(signal, kernel)
|
|
45
|
+
validate_inputs!(signal, kernel)
|
|
46
|
+
ranges = kernel.shape.zip(signal.shape).map { |kernel_size, signal_size| (kernel_size - 1)...signal_size }
|
|
47
|
+
full_convolution = Numo::Pocketfft.fftconvolve(signal, kernel.reverse)
|
|
48
|
+
|
|
49
|
+
Numo::SFloat.cast(full_convolution[*ranges])
|
|
79
50
|
end
|
|
80
|
-
result_shape
|
|
81
|
-
end
|
|
82
51
|
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
52
|
+
# Compatibility alias for the former FFTW3-backed implementation.
|
|
53
|
+
#
|
|
54
|
+
# @deprecated Use {.convolve_fft}; Convolver no longer uses FFTW3.
|
|
55
|
+
# @return [Numo::SFloat] valid cross-correlation result
|
|
56
|
+
def convolve_fftw3(signal, kernel)
|
|
57
|
+
warn 'Convolver.convolve_fftw3 is deprecated; use .convolve_fft instead', uplevel: 1
|
|
58
|
+
convolve_fft(signal, kernel)
|
|
59
|
+
end
|
|
86
60
|
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
61
|
+
# Estimates the relative cost of {.convolve_fft}.
|
|
62
|
+
#
|
|
63
|
+
# @param signal [Numo::NArray] input values
|
|
64
|
+
# @param kernel [Numo::NArray] correlation kernel
|
|
65
|
+
# @return [Float] machine-specific relative cost estimate
|
|
66
|
+
def predict_convolve_fft_time(signal, kernel)
|
|
67
|
+
validate_inputs!(signal, kernel)
|
|
68
|
+
output_size = result_shape(signal.shape, kernel.shape).inject(:*)
|
|
69
|
+
16 * 4.55e-08 * output_size * Math.log(output_size)
|
|
70
|
+
end
|
|
94
71
|
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
72
|
+
# Estimates the relative cost of {.convolve_basic}.
|
|
73
|
+
#
|
|
74
|
+
# @param signal [Numo::NArray] input values
|
|
75
|
+
# @param kernel [Numo::NArray] correlation kernel
|
|
76
|
+
# @return [Float] machine-specific relative cost estimate
|
|
77
|
+
def predict_convolve_basic_time(signal, kernel)
|
|
78
|
+
validate_inputs!(signal, kernel)
|
|
79
|
+
outputs = result_shape(signal.shape, kernel.shape).inject(:*)
|
|
80
|
+
4.54e-12 * (outputs * signal.size * kernel.size)
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
private
|
|
84
|
+
|
|
85
|
+
def result_shape(signal_shape, kernel_shape)
|
|
86
|
+
signal_shape.zip(kernel_shape).map { |signal_size, kernel_size| signal_size - kernel_size + 1 }
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
def validate_inputs!(signal, kernel)
|
|
90
|
+
validate_types!(signal, kernel)
|
|
91
|
+
validate_shapes!(signal, kernel)
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
def validate_types!(signal, kernel)
|
|
95
|
+
unless signal.is_a?(Numo::NArray) && kernel.is_a?(Numo::NArray)
|
|
96
|
+
raise ArgumentError, 'signal and kernel must be Numo::NArray values'
|
|
97
|
+
end
|
|
98
|
+
raise ArgumentError, 'signal and kernel must not be empty' if signal.empty? || kernel.empty?
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
def validate_shapes!(signal, kernel)
|
|
102
|
+
raise ArgumentError, 'signal and kernel must have equal rank' unless signal.ndim == kernel.ndim
|
|
103
|
+
raise ArgumentError, "maximum supported rank is #{MAX_RANK}" if signal.ndim > MAX_RANK
|
|
104
|
+
return if signal.shape.zip(kernel.shape).all? { |signal_size, kernel_size| signal_size >= kernel_size }
|
|
105
|
+
|
|
106
|
+
raise ArgumentError, 'kernel must not be larger than signal in any dimension'
|
|
107
|
+
end
|
|
99
108
|
end
|
|
100
109
|
end
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: convolver
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 1.0.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Neil Slater
|
|
@@ -10,34 +10,47 @@ cert_chain: []
|
|
|
10
10
|
date: 1980-01-02 00:00:00.000000000 Z
|
|
11
11
|
dependencies:
|
|
12
12
|
- !ruby/object:Gem::Dependency
|
|
13
|
-
name:
|
|
13
|
+
name: numo-narray-alt
|
|
14
14
|
requirement: !ruby/object:Gem::Requirement
|
|
15
15
|
requirements:
|
|
16
16
|
- - ">="
|
|
17
17
|
- !ruby/object:Gem::Version
|
|
18
|
-
version:
|
|
18
|
+
version: 0.9.9
|
|
19
|
+
- - "<"
|
|
20
|
+
- !ruby/object:Gem::Version
|
|
21
|
+
version: '0.11'
|
|
19
22
|
type: :runtime
|
|
20
23
|
prerelease: false
|
|
21
24
|
version_requirements: !ruby/object:Gem::Requirement
|
|
22
25
|
requirements:
|
|
23
26
|
- - ">="
|
|
24
27
|
- !ruby/object:Gem::Version
|
|
25
|
-
version:
|
|
28
|
+
version: 0.9.9
|
|
29
|
+
- - "<"
|
|
30
|
+
- !ruby/object:Gem::Version
|
|
31
|
+
version: '0.11'
|
|
26
32
|
- !ruby/object:Gem::Dependency
|
|
27
|
-
name:
|
|
33
|
+
name: numo-pocketfft
|
|
28
34
|
requirement: !ruby/object:Gem::Requirement
|
|
29
35
|
requirements:
|
|
30
36
|
- - ">="
|
|
31
37
|
- !ruby/object:Gem::Version
|
|
32
|
-
version: 0.6
|
|
38
|
+
version: '0.6'
|
|
39
|
+
- - "<"
|
|
40
|
+
- !ruby/object:Gem::Version
|
|
41
|
+
version: '0.8'
|
|
33
42
|
type: :runtime
|
|
34
43
|
prerelease: false
|
|
35
44
|
version_requirements: !ruby/object:Gem::Requirement
|
|
36
45
|
requirements:
|
|
37
46
|
- - ">="
|
|
38
47
|
- !ruby/object:Gem::Version
|
|
39
|
-
version: 0.6
|
|
40
|
-
|
|
48
|
+
version: '0.6'
|
|
49
|
+
- - "<"
|
|
50
|
+
- !ruby/object:Gem::Version
|
|
51
|
+
version: '0.8'
|
|
52
|
+
description: Fast valid cross-correlation for multidimensional Numo::NArray values,
|
|
53
|
+
with native and FFT implementations.
|
|
41
54
|
email:
|
|
42
55
|
- slobo777@gmail.com
|
|
43
56
|
executables: []
|
|
@@ -45,31 +58,21 @@ extensions:
|
|
|
45
58
|
- ext/convolver/extconf.rb
|
|
46
59
|
extra_rdoc_files: []
|
|
47
60
|
files:
|
|
48
|
-
-
|
|
49
|
-
- ".gitignore"
|
|
50
|
-
- ".rubocop.yml"
|
|
51
|
-
- Gemfile
|
|
61
|
+
- CHANGELOG.md
|
|
52
62
|
- LICENSE.txt
|
|
53
63
|
- README.md
|
|
54
|
-
- Rakefile
|
|
55
|
-
- convolver.gemspec
|
|
56
64
|
- ext/convolver/convolve_raw.c
|
|
57
65
|
- ext/convolver/convolve_raw.h
|
|
58
66
|
- ext/convolver/convolver.c
|
|
59
67
|
- ext/convolver/extconf.rb
|
|
60
|
-
- ext/convolver/narray_shared.c
|
|
61
|
-
- ext/convolver/narray_shared.h
|
|
62
68
|
- lib/convolver.rb
|
|
63
69
|
- lib/convolver/version.rb
|
|
64
|
-
|
|
65
|
-
- spec/convolver_fftw3_spec.rb
|
|
66
|
-
- spec/convolver_spec.rb
|
|
67
|
-
- spec/helpers.rb
|
|
68
|
-
homepage: http://github.com/neilslater/convolver
|
|
70
|
+
homepage: https://github.com/neilslater/convolver
|
|
69
71
|
licenses:
|
|
70
72
|
- MIT
|
|
71
73
|
metadata:
|
|
72
74
|
rubygems_mfa_required: 'true'
|
|
75
|
+
source_code_uri: https://github.com/neilslater/convolver
|
|
73
76
|
rdoc_options: []
|
|
74
77
|
require_paths:
|
|
75
78
|
- lib
|
|
@@ -84,7 +87,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
84
87
|
- !ruby/object:Gem::Version
|
|
85
88
|
version: '0'
|
|
86
89
|
requirements: []
|
|
87
|
-
rubygems_version: 4.0.
|
|
90
|
+
rubygems_version: 4.0.16
|
|
88
91
|
specification_version: 4
|
|
89
|
-
summary:
|
|
92
|
+
summary: Fast cross-correlation for Numo::NArray
|
|
90
93
|
test_files: []
|
data/.github/workflows/ci.yml
DELETED
|
@@ -1,40 +0,0 @@
|
|
|
1
|
-
name: CI
|
|
2
|
-
|
|
3
|
-
on:
|
|
4
|
-
pull_request:
|
|
5
|
-
|
|
6
|
-
permissions:
|
|
7
|
-
contents: read
|
|
8
|
-
|
|
9
|
-
jobs:
|
|
10
|
-
test:
|
|
11
|
-
name: Ruby ${{ matrix.ruby }}
|
|
12
|
-
runs-on: ubuntu-latest
|
|
13
|
-
timeout-minutes: 10
|
|
14
|
-
|
|
15
|
-
strategy:
|
|
16
|
-
fail-fast: false
|
|
17
|
-
matrix:
|
|
18
|
-
ruby:
|
|
19
|
-
- '3.3'
|
|
20
|
-
- '3.4'
|
|
21
|
-
|
|
22
|
-
steps:
|
|
23
|
-
- name: Check out repository
|
|
24
|
-
uses: actions/checkout@v4
|
|
25
|
-
|
|
26
|
-
- name: Install FFTW
|
|
27
|
-
run: sudo apt-get update && sudo apt-get install --yes libfftw3-dev
|
|
28
|
-
|
|
29
|
-
- name: Set up Ruby
|
|
30
|
-
uses: ruby/setup-ruby@v1
|
|
31
|
-
with:
|
|
32
|
-
ruby-version: ${{ matrix.ruby }}
|
|
33
|
-
bundler: latest
|
|
34
|
-
bundler-cache: true
|
|
35
|
-
|
|
36
|
-
- name: Compile extension and run specs
|
|
37
|
-
run: bundle exec rake compile test
|
|
38
|
-
|
|
39
|
-
- name: Run RuboCop
|
|
40
|
-
run: bundle exec rubocop
|
data/.gitignore
DELETED
data/.rubocop.yml
DELETED
data/Gemfile
DELETED
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
source 'https://rubygems.org'
|
|
4
|
-
|
|
5
|
-
# Specify your gem's dependencies in convolver.gemspec
|
|
6
|
-
gemspec
|
|
7
|
-
|
|
8
|
-
gem 'rake', '>= 1.9.1'
|
|
9
|
-
gem 'rake-compiler', '>= 0.8.3'
|
|
10
|
-
gem 'rspec', '>= 2.13.0'
|
|
11
|
-
gem 'rubocop', '~> 1.85'
|
|
12
|
-
gem 'rubocop-rake', '~> 0.7'
|
|
13
|
-
gem 'rubocop-rspec', '~> 3.9'
|
|
14
|
-
gem 'simplecov', '>= 0.22'
|
|
15
|
-
gem 'yard', '>= 0.8.7.2'
|
data/Rakefile
DELETED
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
require 'bundler/gem_tasks'
|
|
4
|
-
require 'rspec/core/rake_task'
|
|
5
|
-
require 'rake/extensiontask'
|
|
6
|
-
|
|
7
|
-
desc 'Convolver unit tests'
|
|
8
|
-
RSpec::Core::RakeTask.new(:test) do |t|
|
|
9
|
-
t.pattern = 'spec/*_spec.rb'
|
|
10
|
-
t.verbose = true
|
|
11
|
-
end
|
|
12
|
-
|
|
13
|
-
gemspec = Gem::Specification.load('convolver.gemspec')
|
|
14
|
-
Rake::ExtensionTask.new do |ext|
|
|
15
|
-
ext.name = 'convolver'
|
|
16
|
-
ext.source_pattern = '*.{c,h}'
|
|
17
|
-
ext.ext_dir = 'ext/convolver'
|
|
18
|
-
ext.lib_dir = 'lib/convolver'
|
|
19
|
-
ext.gem_spec = gemspec
|
|
20
|
-
end
|
|
21
|
-
|
|
22
|
-
task default: %i[compile test]
|
data/convolver.gemspec
DELETED
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
require 'English'
|
|
4
|
-
lib = File.expand_path('lib', __dir__)
|
|
5
|
-
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
|
6
|
-
require 'convolver/version'
|
|
7
|
-
|
|
8
|
-
Gem::Specification.new do |spec|
|
|
9
|
-
spec.name = 'convolver'
|
|
10
|
-
spec.version = Convolver::VERSION
|
|
11
|
-
spec.authors = ['Neil Slater']
|
|
12
|
-
spec.email = ['slobo777@gmail.com']
|
|
13
|
-
spec.description = 'Convolution for NArray'
|
|
14
|
-
spec.summary = 'Convolution for NArray'
|
|
15
|
-
spec.homepage = 'http://github.com/neilslater/convolver'
|
|
16
|
-
spec.license = 'MIT'
|
|
17
|
-
spec.required_ruby_version = '>= 3.2'
|
|
18
|
-
|
|
19
|
-
spec.add_dependency 'fftw3', '>= 0.3'
|
|
20
|
-
spec.add_dependency 'narray', '>= 0.6.0.8'
|
|
21
|
-
|
|
22
|
-
spec.files = `git ls-files`.split($INPUT_RECORD_SEPARATOR)
|
|
23
|
-
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
|
24
|
-
spec.extensions = spec.files.grep(%r{/extconf\.rb$})
|
|
25
|
-
spec.require_paths = ['lib']
|
|
26
|
-
spec.metadata['rubygems_mfa_required'] = 'true'
|
|
27
|
-
end
|
|
@@ -1,61 +0,0 @@
|
|
|
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
|
-
}
|
|
@@ -1,22 +0,0 @@
|
|
|
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
|
|
@@ -1,89 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
require 'helpers'
|
|
4
|
-
|
|
5
|
-
describe Convolver do
|
|
6
|
-
describe '#convolve_basic' do
|
|
7
|
-
it 'works like the example in the README' do
|
|
8
|
-
a = NArray[0.3, 0.4, 0.5]
|
|
9
|
-
b = NArray[1.3, -0.5]
|
|
10
|
-
c = described_class.convolve_basic(a, b)
|
|
11
|
-
expect(c).to be_narray_like NArray[0.19, 0.27]
|
|
12
|
-
end
|
|
13
|
-
|
|
14
|
-
it 'calculates a 2D convolution' do
|
|
15
|
-
a = NArray[[0.3, 0.4, 0.5], [0.6, 0.8, 0.2], [0.9, 1.0, 0.1]]
|
|
16
|
-
b = NArray[[1.2, -0.5], [0.5, -1.3]]
|
|
17
|
-
c = described_class.convolve_basic(a, b)
|
|
18
|
-
expect(c).to be_narray_like NArray[[-0.58, 0.37], [-0.53, 1.23]]
|
|
19
|
-
end
|
|
20
|
-
|
|
21
|
-
it 'calculates a 2D convolution with rectangular arrays' do
|
|
22
|
-
a = NArray[ [0.3, 0.4, 0.5, 0.3, 0.4], [0.6, 0.8, 0.2, 0.8, 0.2],
|
|
23
|
-
[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],
|
|
24
|
-
[0.4, 0.5, 0.6, 0.7, 0.8], [0.5, 0.4, 0.3, 0.2, 0.1] ]
|
|
25
|
-
b = NArray[[1.2, -0.5, 0.2], [1.8, 0.5, -1.3]]
|
|
26
|
-
c = described_class.convolve_basic(a, b)
|
|
27
|
-
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],
|
|
28
|
-
[1.13, 1.3, 0.83], [1.04, 0.26, 0.77], [1.06, 1.05, 1.04] ]
|
|
29
|
-
end
|
|
30
|
-
|
|
31
|
-
it 'calculates a 3D convolution' do
|
|
32
|
-
# 5x4x3
|
|
33
|
-
a = NArray[
|
|
34
|
-
[[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],
|
|
35
|
-
[0.5, 0.9, 0.2, 0.1, 0.6]],
|
|
36
|
-
[[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],
|
|
37
|
-
[0.1, 0.9, 0.7, 0.1, 0.3]],
|
|
38
|
-
[[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],
|
|
39
|
-
[0.2, 0.5, 0.4, 0.7, 0.2]]
|
|
40
|
-
]
|
|
41
|
-
|
|
42
|
-
# 3x3x3
|
|
43
|
-
b = NArray[
|
|
44
|
-
[[-0.9, 1.2, 0.8], [0.9, 0.1, -0.5], [1.1, 0.1, -1.1]],
|
|
45
|
-
[[-0.2, -1.0, 1.4], [-1.4, 0.0, 1.3], [0.3, 1.0, -0.5]],
|
|
46
|
-
[[0.6, 0.0, 0.7], [-0.7, 1.1, 1.2], [1.3, 0.7, 0.0]]
|
|
47
|
-
]
|
|
48
|
-
|
|
49
|
-
# Should be 3x2x1
|
|
50
|
-
c = described_class.convolve_basic(a, b)
|
|
51
|
-
expect(c).to be_narray_like NArray[[[5.51, 3.04, 4.3], [3.04, 6.31, 3.87]]]
|
|
52
|
-
end
|
|
53
|
-
|
|
54
|
-
it 'calculates a 4D convolution' do
|
|
55
|
-
# 3x4x5x3
|
|
56
|
-
a = NArray[
|
|
57
|
-
[[[0.5, 0.4, 0.9], [0.1, 0.9, 0.8], [0.4, 0.0, 0.1], [0.8, 0.3, 0.4]],
|
|
58
|
-
[[0.0, 0.4, 0.0], [0.2, 0.3, 0.8], [0.6, 0.3, 0.2], [0.7, 0.4, 0.3]],
|
|
59
|
-
[[0.3, 0.3, 0.1], [0.6, 0.9, 0.4], [0.4, 0.0, 0.1], [0.8, 0.3, 0.4]],
|
|
60
|
-
[[0.0, 0.4, 0.0], [0.2, 0.3, 0.8], [0.6, 0.3, 0.2], [0.7, 0.4, 0.3]],
|
|
61
|
-
[[0.3, 0.3, 0.1], [0.6, 0.9, 0.4], [0.4, 0.0, 0.1], [0.8, 0.3, 0.4]]],
|
|
62
|
-
[[[0.5, 0.4, 0.9], [0.1, 0.9, 0.8], [0.4, 0.0, 0.1], [0.8, 0.3, 0.4]],
|
|
63
|
-
[[0.0, 0.4, 0.0], [0.2, 0.3, 0.8], [0.6, 0.3, 0.2], [0.7, 0.4, 0.3]],
|
|
64
|
-
[[0.3, 0.3, 0.1], [0.6, 0.9, 0.4], [0.4, 0.0, 0.1], [0.8, 0.3, 0.4]],
|
|
65
|
-
[[0.0, 0.4, 0.0], [0.2, 0.3, 0.8], [0.6, 0.3, 0.2], [0.7, 0.4, 0.3]],
|
|
66
|
-
[[0.3, 0.3, 0.1], [0.6, 0.9, 0.4], [0.4, 0.0, 0.1], [0.8, 0.3, 0.4]]],
|
|
67
|
-
[[[0.5, 0.4, 0.9], [0.1, 0.9, 0.8], [0.4, 0.0, 0.1], [0.8, 0.3, 0.4]],
|
|
68
|
-
[[0.0, 0.4, 0.0], [0.2, 0.3, 0.8], [0.6, 0.3, 0.2], [0.7, 0.4, 0.3]],
|
|
69
|
-
[[0.3, 0.3, 0.1], [0.6, 0.9, 0.4], [0.4, 0.0, 0.1], [0.8, 0.3, 0.4]],
|
|
70
|
-
[[0.0, 0.4, 0.0], [0.2, 0.3, 0.8], [0.6, 0.3, 0.2], [0.7, 0.4, 0.3]],
|
|
71
|
-
[[0.3, 0.3, 0.1], [0.6, 0.9, 0.4], [0.4, 0.0, 0.1], [0.8, 0.3, 0.4]]] ]
|
|
72
|
-
|
|
73
|
-
# 2x3x3x2
|
|
74
|
-
b = NArray[ [
|
|
75
|
-
[[1.1, 0.6], [1.2, 0.6], [0.8, 0.1]], [[-0.4, 0.8], [0.5, 0.4], [1.2, 0.2]],
|
|
76
|
-
[[0.8, 0.2], [0.5, 0.0], [1.4, 1.3]]
|
|
77
|
-
],
|
|
78
|
-
[[[1.1, 0.6], [1.2, 0.6], [0.8, 0.1]], [[-0.4, 0.8], [0.5, 0.4], [1.2, 0.2]],
|
|
79
|
-
[[0.8, 0.2], [0.5, 0.0], [1.4, 1.3]]] ]
|
|
80
|
-
|
|
81
|
-
# Should be 2x2x3x2
|
|
82
|
-
c = described_class.convolve_basic(a, b)
|
|
83
|
-
expect(c).to be_narray_like NArray[
|
|
84
|
-
[[[8.5, 8.2], [11.34, 9.68]], [[7.68, 6.56], [11.24, 7.16]], [[9.14, 6.54], [12.44, 9.2]]],
|
|
85
|
-
[[[8.5, 8.2], [11.34, 9.68]], [[7.68, 6.56], [11.24, 7.16]], [[9.14, 6.54], [12.44, 9.2]]]
|
|
86
|
-
]
|
|
87
|
-
end
|
|
88
|
-
end
|
|
89
|
-
end
|