CooCoo 0.1.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 +7 -0
- data/.gitignore +16 -0
- data/CooCoo.gemspec +47 -0
- data/Gemfile +4 -0
- data/Gemfile.lock +88 -0
- data/README.md +123 -0
- data/Rakefile +81 -0
- data/bin/cuda-dev-info +25 -0
- data/bin/cuda-free +28 -0
- data/bin/cuda-free-trend +7 -0
- data/bin/ffi-gen +267 -0
- data/bin/spec_runner_html.sh +42 -0
- data/bin/trainer +198 -0
- data/bin/trend-cost +13 -0
- data/examples/char-rnn.rb +405 -0
- data/examples/cifar/cifar.rb +94 -0
- data/examples/img-similarity.rb +201 -0
- data/examples/math_ops.rb +57 -0
- data/examples/mnist.rb +365 -0
- data/examples/mnist_classifier.rb +293 -0
- data/examples/mnist_dream.rb +214 -0
- data/examples/seeds.rb +268 -0
- data/examples/seeds_dataset.txt +210 -0
- data/examples/t10k-images-idx3-ubyte +0 -0
- data/examples/t10k-labels-idx1-ubyte +0 -0
- data/examples/train-images-idx3-ubyte +0 -0
- data/examples/train-labels-idx1-ubyte +0 -0
- data/ext/buffer/Rakefile +50 -0
- data/ext/buffer/buffer.pre.cu +727 -0
- data/ext/buffer/matrix.pre.cu +49 -0
- data/lib/CooCoo.rb +1 -0
- data/lib/coo-coo.rb +18 -0
- data/lib/coo-coo/activation_functions.rb +344 -0
- data/lib/coo-coo/consts.rb +5 -0
- data/lib/coo-coo/convolution.rb +298 -0
- data/lib/coo-coo/core_ext.rb +75 -0
- data/lib/coo-coo/cost_functions.rb +91 -0
- data/lib/coo-coo/cuda.rb +116 -0
- data/lib/coo-coo/cuda/device_buffer.rb +240 -0
- data/lib/coo-coo/cuda/device_buffer/ffi.rb +109 -0
- data/lib/coo-coo/cuda/error.rb +51 -0
- data/lib/coo-coo/cuda/host_buffer.rb +117 -0
- data/lib/coo-coo/cuda/runtime.rb +157 -0
- data/lib/coo-coo/cuda/vector.rb +315 -0
- data/lib/coo-coo/data_sources.rb +2 -0
- data/lib/coo-coo/data_sources/xournal.rb +25 -0
- data/lib/coo-coo/data_sources/xournal/bitmap_stream.rb +197 -0
- data/lib/coo-coo/data_sources/xournal/document.rb +377 -0
- data/lib/coo-coo/data_sources/xournal/loader.rb +144 -0
- data/lib/coo-coo/data_sources/xournal/renderer.rb +101 -0
- data/lib/coo-coo/data_sources/xournal/saver.rb +99 -0
- data/lib/coo-coo/data_sources/xournal/training_document.rb +78 -0
- data/lib/coo-coo/data_sources/xournal/training_document/constants.rb +15 -0
- data/lib/coo-coo/data_sources/xournal/training_document/document_maker.rb +89 -0
- data/lib/coo-coo/data_sources/xournal/training_document/document_reader.rb +105 -0
- data/lib/coo-coo/data_sources/xournal/training_document/example.rb +37 -0
- data/lib/coo-coo/data_sources/xournal/training_document/sets.rb +76 -0
- data/lib/coo-coo/debug.rb +8 -0
- data/lib/coo-coo/dot.rb +129 -0
- data/lib/coo-coo/drawing.rb +4 -0
- data/lib/coo-coo/drawing/cairo_canvas.rb +100 -0
- data/lib/coo-coo/drawing/canvas.rb +68 -0
- data/lib/coo-coo/drawing/chunky_canvas.rb +101 -0
- data/lib/coo-coo/drawing/sixel.rb +214 -0
- data/lib/coo-coo/enum.rb +17 -0
- data/lib/coo-coo/from_name.rb +58 -0
- data/lib/coo-coo/fully_connected_layer.rb +205 -0
- data/lib/coo-coo/generation_script.rb +38 -0
- data/lib/coo-coo/grapher.rb +140 -0
- data/lib/coo-coo/image.rb +286 -0
- data/lib/coo-coo/layer.rb +67 -0
- data/lib/coo-coo/layer_factory.rb +26 -0
- data/lib/coo-coo/linear_layer.rb +59 -0
- data/lib/coo-coo/math.rb +607 -0
- data/lib/coo-coo/math/abstract_vector.rb +121 -0
- data/lib/coo-coo/math/functions.rb +39 -0
- data/lib/coo-coo/math/interpolation.rb +7 -0
- data/lib/coo-coo/network.rb +264 -0
- data/lib/coo-coo/neuron.rb +112 -0
- data/lib/coo-coo/neuron_layer.rb +168 -0
- data/lib/coo-coo/option_parser.rb +18 -0
- data/lib/coo-coo/platform.rb +17 -0
- data/lib/coo-coo/progress_bar.rb +11 -0
- data/lib/coo-coo/recurrence/backend.rb +99 -0
- data/lib/coo-coo/recurrence/frontend.rb +101 -0
- data/lib/coo-coo/sequence.rb +187 -0
- data/lib/coo-coo/shell.rb +2 -0
- data/lib/coo-coo/temporal_network.rb +291 -0
- data/lib/coo-coo/trainer.rb +21 -0
- data/lib/coo-coo/trainer/base.rb +67 -0
- data/lib/coo-coo/trainer/batch.rb +82 -0
- data/lib/coo-coo/trainer/batch_stats.rb +27 -0
- data/lib/coo-coo/trainer/momentum_stochastic.rb +59 -0
- data/lib/coo-coo/trainer/stochastic.rb +47 -0
- data/lib/coo-coo/transformer.rb +272 -0
- data/lib/coo-coo/vector_layer.rb +194 -0
- data/lib/coo-coo/version.rb +3 -0
- data/lib/coo-coo/weight_deltas.rb +23 -0
- data/prototypes/convolution.rb +116 -0
- data/prototypes/linear_drop.rb +51 -0
- data/prototypes/recurrent_layers.rb +79 -0
- data/www/images/screamer.png +0 -0
- data/www/images/screamer.xcf +0 -0
- data/www/index.html +82 -0
- metadata +373 -0
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
class Numeric
|
|
2
|
+
[ :exp, :sqrt, :log, :log10, :log2,
|
|
3
|
+
:sin, :asin, :cos, :acos, :tan, :atan,
|
|
4
|
+
:sinh, :asinh, :cosh, :acosh, :tanh, :atanh,
|
|
5
|
+
:ceil, :floor, :round
|
|
6
|
+
].each do |f|
|
|
7
|
+
define_method(f) do
|
|
8
|
+
::Math.send(f, self)
|
|
9
|
+
end
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def identity
|
|
13
|
+
coerce(1)[0]
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def zero
|
|
17
|
+
coerce(0)[0]
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
class Object
|
|
22
|
+
def self.instance_defines?(method)
|
|
23
|
+
instance_methods.include?(method)
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def self.define_once(method, &definition)
|
|
27
|
+
unless instance_defines?(method)
|
|
28
|
+
define_method(method, &definition)
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def self.delegate(*args)
|
|
33
|
+
opts = args.pop
|
|
34
|
+
|
|
35
|
+
args.each do |meth|
|
|
36
|
+
define_method(meth) do |*a|
|
|
37
|
+
send(opts[:to]).send(meth, *a)
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
class Array
|
|
44
|
+
def zero
|
|
45
|
+
self.class.new(size, 0.0)
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
class File
|
|
50
|
+
def self.write_to(path, &block)
|
|
51
|
+
tmp = path.to_s + ".tmp"
|
|
52
|
+
bak = path.to_s + "~"
|
|
53
|
+
|
|
54
|
+
# write to temp file
|
|
55
|
+
File.open(tmp, "w", &block)
|
|
56
|
+
|
|
57
|
+
# create a backup file
|
|
58
|
+
if File.exists?(path)
|
|
59
|
+
# remove any existing backup
|
|
60
|
+
if File.exists?(bak)
|
|
61
|
+
File.delete(bak)
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
File.rename(path, bak)
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
# finalize the save
|
|
68
|
+
File.rename(tmp, path)
|
|
69
|
+
|
|
70
|
+
self
|
|
71
|
+
rescue
|
|
72
|
+
File.delete(tmp)
|
|
73
|
+
raise
|
|
74
|
+
end
|
|
75
|
+
end
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
require 'coo-coo/from_name'
|
|
2
|
+
|
|
3
|
+
module CooCoo
|
|
4
|
+
# CostFunctions are used with a {Trainer} to determine how close a {Network}
|
|
5
|
+
# is coming to its target. CostFunctions are functions of two variables.
|
|
6
|
+
#
|
|
7
|
+
# To get a cost function instance use the included {#from_name}.
|
|
8
|
+
# Then you can +#call+ or +#derivative+ any cost function.
|
|
9
|
+
#
|
|
10
|
+
# To create a new cost function that can be used with a {Trainer},
|
|
11
|
+
# you must call {CostFunctions.register} and implement the
|
|
12
|
+
# +#call+ and +#derivative+ class methods.
|
|
13
|
+
module CostFunctions
|
|
14
|
+
class << self
|
|
15
|
+
include FromName
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
# @abstract Defines and documents the cost functions' interface.
|
|
19
|
+
# Be sure to call {CostFunctions.register} inside your subclass.
|
|
20
|
+
class Base
|
|
21
|
+
# Returns the cost between the target output and actual output.
|
|
22
|
+
#
|
|
23
|
+
# @param target [Vector] Desired value
|
|
24
|
+
# @param x [Vector] A network's actual output
|
|
25
|
+
# @return [Vector] The cost of the target for this output
|
|
26
|
+
def self.call(target, x)
|
|
27
|
+
raise NotImplementedError.new
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
# Returns the derivative of the cost function, +#call+. This is
|
|
31
|
+
# what gets fed into the network to determine the changes.
|
|
32
|
+
#
|
|
33
|
+
# @param target [Vector] Desired value
|
|
34
|
+
# @param x [Vector] A network's actual output
|
|
35
|
+
# @param y [Vector] The results from a previous +#call+
|
|
36
|
+
# @return [Vector]
|
|
37
|
+
def self.derivative(target, x, y = nil)
|
|
38
|
+
raise NotImplementedError.new
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
# Implements the mean square cost function. Its derivative is
|
|
43
|
+
# a simple difference between the target and actual output.
|
|
44
|
+
class MeanSquare < Base
|
|
45
|
+
CostFunctions.register(self, name)
|
|
46
|
+
|
|
47
|
+
def self.call(target, x)
|
|
48
|
+
d = derivative(target, x)
|
|
49
|
+
d * d * 0.5
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def self.derivative(target, x, y = nil)
|
|
53
|
+
x - target
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
# Implements the log cross-entropy cost function that is used with
|
|
58
|
+
# {ActivationFunctions::SoftMax} and
|
|
59
|
+
# {ActivationFunctions::ShiftedSoftMax}. This calls +Math.log+ on
|
|
60
|
+
# the network's output and multiples that by the target. Therefore
|
|
61
|
+
# good target values are +0...1+.
|
|
62
|
+
class CrossEntropy < Base
|
|
63
|
+
CostFunctions.register(self, name)
|
|
64
|
+
|
|
65
|
+
def self.call(target, x)
|
|
66
|
+
-x.log * target
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
def self.derivative(target, x)
|
|
70
|
+
-target / x
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
# Combines a SoftMax activation with CrossEntropy. Due to math this
|
|
75
|
+
# is more optimal than having a SoftMax layer and doing CrossEntropy
|
|
76
|
+
# seperately.
|
|
77
|
+
#
|
|
78
|
+
# @see http://peterroelants.github.io/posts/neural_network_implementation_intermezzo02/
|
|
79
|
+
class SoftMaxCrossEntropy < CrossEntropy
|
|
80
|
+
CostFunctions.register(self, name)
|
|
81
|
+
|
|
82
|
+
def self.call(target, x)
|
|
83
|
+
super(target, ActivationFunctions::ShiftedSoftMax.call(x))
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
def self.derivative(target, x)
|
|
87
|
+
x - target
|
|
88
|
+
end
|
|
89
|
+
end
|
|
90
|
+
end
|
|
91
|
+
end
|
data/lib/coo-coo/cuda.rb
ADDED
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
require 'coo-coo/debug'
|
|
2
|
+
begin
|
|
3
|
+
require 'coo-coo/cuda/runtime'
|
|
4
|
+
|
|
5
|
+
module CooCoo
|
|
6
|
+
module CUDA
|
|
7
|
+
def self.available?
|
|
8
|
+
ENV["COOCOO_USE_CUDA"] != "0"# && Runtime.device_count > 0
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def self.memory_info
|
|
12
|
+
Runtime.memory_info
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def self.collect_garbage(size = nil)
|
|
16
|
+
free, total = memory_info
|
|
17
|
+
if size == nil || (3 * size + free) >= total
|
|
18
|
+
GC.start
|
|
19
|
+
new_free, total = memory_info
|
|
20
|
+
diff = free - new_free
|
|
21
|
+
if size && (size + new_free) >= total
|
|
22
|
+
raise NoMemoryError.new(size)
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
require 'coo-coo/cuda/host_buffer'
|
|
30
|
+
require 'coo-coo/cuda/device_buffer'
|
|
31
|
+
require 'coo-coo/cuda/vector'
|
|
32
|
+
|
|
33
|
+
rescue LoadError
|
|
34
|
+
CooCoo.debug("LoadError #{__FILE__}: #{$!}")
|
|
35
|
+
module CooCoo
|
|
36
|
+
module CUDA
|
|
37
|
+
def self.available?
|
|
38
|
+
false
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
if __FILE__ == $0
|
|
45
|
+
require 'pp'
|
|
46
|
+
|
|
47
|
+
puts("Cuda not available") unless CooCoo::CUDA.available?
|
|
48
|
+
|
|
49
|
+
puts("Resetting #{CooCoo::CUDA::Runtime.cudaDeviceReset}")
|
|
50
|
+
puts("Device = #{CooCoo::CUDA::Runtime.get_device} / #{CooCoo::CUDA::Runtime.device_count}")
|
|
51
|
+
puts("Init = #{CooCoo::CUDA::DeviceBuffer::FFI.buffer_init(0)}")
|
|
52
|
+
puts("Block size = #{CooCoo::CUDA::DeviceBuffer::FFI.buffer_block_size}")
|
|
53
|
+
puts("Grid size = #{CooCoo::CUDA::DeviceBuffer::FFI.buffer_max_grid_size}")
|
|
54
|
+
puts("Total memory = #{CooCoo::CUDA.memory_info.join('/')}")
|
|
55
|
+
props = CooCoo::CUDA::Runtime::DeviceProperties.new
|
|
56
|
+
puts(CooCoo::CUDA::Runtime.cudaGetDeviceProperties(props, 0).inspect)
|
|
57
|
+
puts("Properties")
|
|
58
|
+
props.members.each do |m|
|
|
59
|
+
value = props[m]
|
|
60
|
+
if m != :name && value.kind_of?(FFI::Struct::InlineArray)
|
|
61
|
+
value.each_with_index do |v, i|
|
|
62
|
+
puts("#{m}[#{i}]\t#{v}")
|
|
63
|
+
end
|
|
64
|
+
else
|
|
65
|
+
puts("#{m}\t#{value}")
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
dev = CooCoo::CUDA::Runtime.get_device
|
|
69
|
+
puts("Device #{dev}")
|
|
70
|
+
puts("Creating")
|
|
71
|
+
WIDTH = 256
|
|
72
|
+
HEIGHT = 256
|
|
73
|
+
SIZE = WIDTH * HEIGHT # 1024 * 1024 * 1
|
|
74
|
+
h = CooCoo::CUDA::HostBuffer.new(SIZE)
|
|
75
|
+
arr = SIZE.times.collect { |n| n }
|
|
76
|
+
h.set(arr)
|
|
77
|
+
a = CooCoo::CUDA::Vector.new(SIZE)
|
|
78
|
+
a.set(h)
|
|
79
|
+
puts("Size = #{a.size}")
|
|
80
|
+
puts("Getting")
|
|
81
|
+
b = ((a.dot(WIDTH, HEIGHT, a) * 3 - a) / 3.0).sin #* 2 + 1
|
|
82
|
+
#b = b.get.to_a
|
|
83
|
+
puts(b[0, 10].to_s)
|
|
84
|
+
puts(b[-10, 10].to_s)
|
|
85
|
+
puts("Sum = #{b.sum} #{b.each.sum}")
|
|
86
|
+
|
|
87
|
+
require 'benchmark'
|
|
88
|
+
require 'coo-coo/math'
|
|
89
|
+
require 'nmatrix'
|
|
90
|
+
|
|
91
|
+
Benchmark.bm(3) do |bm|
|
|
92
|
+
bm.report("cuda add") do
|
|
93
|
+
b = a.clone
|
|
94
|
+
10000.times do |i|
|
|
95
|
+
#puts("%i %i" % [ CooCoo::CUDA::DeviceBuffer::FFI.buffer_total_bytes_allocated, CooCoo::CUDA::Runtime.total_global_mem ]) if i % 1000
|
|
96
|
+
b = b + b
|
|
97
|
+
end
|
|
98
|
+
#puts("CUDA sum", b.get.to_a.inspect)
|
|
99
|
+
#puts("Last error: ", CooCoo::CUDA::FFI.cudaGetLastError)
|
|
100
|
+
end
|
|
101
|
+
bm.report("ruby vector add") do
|
|
102
|
+
b = CooCoo::Ruby::Vector[arr]
|
|
103
|
+
10000.times do
|
|
104
|
+
b = b + b
|
|
105
|
+
end
|
|
106
|
+
#puts("Vector sum", b.inspect)
|
|
107
|
+
end
|
|
108
|
+
bm.report("nmatrix add") do
|
|
109
|
+
b = NMatrix[arr]
|
|
110
|
+
10000.times do
|
|
111
|
+
b = b + b
|
|
112
|
+
end
|
|
113
|
+
#puts("NMatrix sum", b.inspect)
|
|
114
|
+
end
|
|
115
|
+
end
|
|
116
|
+
end
|
|
@@ -0,0 +1,240 @@
|
|
|
1
|
+
require 'pathname'
|
|
2
|
+
require 'ffi'
|
|
3
|
+
require 'coo-coo/cuda/error'
|
|
4
|
+
require 'coo-coo/cuda/host_buffer'
|
|
5
|
+
|
|
6
|
+
module CooCoo
|
|
7
|
+
module CUDA
|
|
8
|
+
class DeviceBuffer < ::FFI::Struct
|
|
9
|
+
layout(:data, :pointer,
|
|
10
|
+
:size, :size_t)
|
|
11
|
+
|
|
12
|
+
def self.create(size, initial_value = 0.0)
|
|
13
|
+
FFI.new(size, initial_value.to_f)
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def self.release(ptr)
|
|
17
|
+
FFI.buffer_free(ptr)
|
|
18
|
+
rescue
|
|
19
|
+
CooCoo.debug(__method__, $!.inspect)
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
require 'coo-coo/cuda/device_buffer/ffi'
|
|
23
|
+
|
|
24
|
+
def size
|
|
25
|
+
FFI.buffer_length(self)
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def clone
|
|
29
|
+
self.class.
|
|
30
|
+
create(self.size).
|
|
31
|
+
set(self)
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def self.[](other, length = nil)
|
|
35
|
+
if other.respond_to?(:each)
|
|
36
|
+
length ||= other.size
|
|
37
|
+
else
|
|
38
|
+
length ||= 1
|
|
39
|
+
end
|
|
40
|
+
self.create(length).set(other)
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def set(buffer)
|
|
44
|
+
case buffer
|
|
45
|
+
when self.class then FFI.set(self, buffer)
|
|
46
|
+
when Numeric then FFI.setd(self, buffer.to_f, 0, size)
|
|
47
|
+
else
|
|
48
|
+
buffer = HostBuffer[buffer]
|
|
49
|
+
FFI.setv(self, buffer.to_ptr, buffer.size)
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
self
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def []=(index, value, length = nil)
|
|
56
|
+
index = size + index if index < 0
|
|
57
|
+
raise RangeError.new("#{index} >= #{size}") if index >= size
|
|
58
|
+
raise RangeError.new("#{index} < 0") if index < 0
|
|
59
|
+
|
|
60
|
+
if length
|
|
61
|
+
value, length = length, value
|
|
62
|
+
if value.kind_of?(self.class)
|
|
63
|
+
FFI.setn(self, index, value, length)
|
|
64
|
+
else
|
|
65
|
+
buffer = HostBuffer[value, length]
|
|
66
|
+
FFI.setvn(self, index, buffer.to_ptr, buffer.size)
|
|
67
|
+
end
|
|
68
|
+
else
|
|
69
|
+
FFI.set_element(self, index, value)
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
def get
|
|
74
|
+
out = HostBuffer.new(size)
|
|
75
|
+
FFI.get(self, out.to_ptr, size)
|
|
76
|
+
out
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
def [](index, len = nil, pad = false)
|
|
80
|
+
return super(index) if index.kind_of?(Symbol)
|
|
81
|
+
|
|
82
|
+
index = size + index if index < 0
|
|
83
|
+
raise RangeError.new if index >= size || index < 0
|
|
84
|
+
if len
|
|
85
|
+
len = (size - index) if pad == false && (index + len) >= size
|
|
86
|
+
raise ArgumentError.new("length must be > 0") if len <= 0
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
if len
|
|
90
|
+
FFI.slice(self, index, len)
|
|
91
|
+
else
|
|
92
|
+
out = HostBuffer.new(1)
|
|
93
|
+
FFI.host_slice(self, out.to_ptr, index, 1)
|
|
94
|
+
out[0]
|
|
95
|
+
end
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
def each(&block)
|
|
99
|
+
get.each(&block)
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
def each_slice(n, &block)
|
|
103
|
+
return to_enum(__method__, n) unless block
|
|
104
|
+
|
|
105
|
+
(size / n.to_f).ceil.to_i.times do |i|
|
|
106
|
+
block.call(self[i * n, n, true])
|
|
107
|
+
end
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
def sum
|
|
111
|
+
FFI.buffer_sum(self)
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
def dot(w, h, other, ow = nil, oh = nil)
|
|
115
|
+
if other.kind_of?(self.class)
|
|
116
|
+
ow ||= w
|
|
117
|
+
oh ||= h
|
|
118
|
+
raise ArgumentError.new("width (#{w}) must match the other's height (#{oh})") if w != oh
|
|
119
|
+
raise ArgumentError.new("width * height != size") if size != w * h
|
|
120
|
+
raise ArgumentError.new("other's width * height != other's size (#{ow} * #{oh} != #{other.size})") if other.size != ow * oh
|
|
121
|
+
raise ArgumentError.new("other is null") if other.null?
|
|
122
|
+
raise ArgumentError.new("self is null") if null?
|
|
123
|
+
|
|
124
|
+
FFI.dot(self, w, h, other, ow, oh)
|
|
125
|
+
else
|
|
126
|
+
b, a = coerce(other)
|
|
127
|
+
dot(w, h, b, ow, oh)
|
|
128
|
+
end
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
def slice_2d(width, height, x, y, out_width, out_height, initial = 0.0)
|
|
132
|
+
FFI.slice_2d(self, width, height, x, y, out_width, out_height, initial)
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
def set2d!(width, src, src_width, x, y)
|
|
136
|
+
case src
|
|
137
|
+
when self.class then FFI.set2d(self, width, src, src_width, x, y)
|
|
138
|
+
else
|
|
139
|
+
src = HostBuffer[src] unless src.kind_of?(HostBuffer)
|
|
140
|
+
FFI.set2dv(self, width, src.to_ptr, src_width, src.size / src_width, x, y)
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
self
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
def ==(other)
|
|
147
|
+
if other.kind_of?(self.class)
|
|
148
|
+
1 == FFI.buffer_eq(self, other)
|
|
149
|
+
else
|
|
150
|
+
return false
|
|
151
|
+
end
|
|
152
|
+
end
|
|
153
|
+
|
|
154
|
+
{ :< => "lt",
|
|
155
|
+
:<= => "lte",
|
|
156
|
+
:>= => "gte",
|
|
157
|
+
:> => "gt",
|
|
158
|
+
:collect_equal? => 'eq',
|
|
159
|
+
:collect_not_equal? => 'neq'
|
|
160
|
+
}.each do |comp_op, func|
|
|
161
|
+
define_method(comp_op) do |other|
|
|
162
|
+
if other.kind_of?(self.class)
|
|
163
|
+
FFI.send("collect_#{func}", self, other)
|
|
164
|
+
elsif other.kind_of?(Numeric)
|
|
165
|
+
FFI.send("collect_#{func}d", self, other)
|
|
166
|
+
else
|
|
167
|
+
raise TypeError.new("wrong type #{other.class}")
|
|
168
|
+
end
|
|
169
|
+
end
|
|
170
|
+
end
|
|
171
|
+
|
|
172
|
+
[ :abs, :exp, :log, :log10, :log2, :sqrt,
|
|
173
|
+
:sin, :asin, :cos, :acos, :tan, :atan,
|
|
174
|
+
:sinh, :asinh, :cosh, :acosh, :tanh, :atanh,
|
|
175
|
+
:ceil, :floor, :round,
|
|
176
|
+
:collect_nan, :collect_inf
|
|
177
|
+
].each do |f|
|
|
178
|
+
define_method(f) do
|
|
179
|
+
r = FFI.send(f, self)
|
|
180
|
+
raise NullResultError.new("NULL result") if r.null?
|
|
181
|
+
r
|
|
182
|
+
end
|
|
183
|
+
end
|
|
184
|
+
|
|
185
|
+
def coerce(other)
|
|
186
|
+
if other.respond_to?(:each)
|
|
187
|
+
return self.class[other], self
|
|
188
|
+
else
|
|
189
|
+
return self.class.create(self.size).set(other), self
|
|
190
|
+
end
|
|
191
|
+
end
|
|
192
|
+
|
|
193
|
+
def to_a
|
|
194
|
+
get.to_a
|
|
195
|
+
end
|
|
196
|
+
|
|
197
|
+
def null?
|
|
198
|
+
super || self[:data].null?
|
|
199
|
+
end
|
|
200
|
+
|
|
201
|
+
def self.ffi_operator(op, ffi_method)
|
|
202
|
+
define_method(op) do |other|
|
|
203
|
+
if other.respond_to?(:each)
|
|
204
|
+
other = self.class[other] unless other.kind_of?(self.class)
|
|
205
|
+
raise ArgumentError.new("size mismatch: #{size} != #{other.size}") if size != other.size
|
|
206
|
+
FFI.send(ffi_method, self, other)
|
|
207
|
+
else
|
|
208
|
+
FFI.send(ffi_method.to_s + "d", self, other.to_f)
|
|
209
|
+
end
|
|
210
|
+
end
|
|
211
|
+
end
|
|
212
|
+
|
|
213
|
+
ffi_operator(:+, :add)
|
|
214
|
+
ffi_operator(:-, :sub)
|
|
215
|
+
ffi_operator(:*, :mul)
|
|
216
|
+
ffi_operator(:**, :pow)
|
|
217
|
+
ffi_operator(:/, :div)
|
|
218
|
+
|
|
219
|
+
def self.identity(w, h)
|
|
220
|
+
FFI.buffer_identity(w, h)
|
|
221
|
+
end
|
|
222
|
+
|
|
223
|
+
def diagflat
|
|
224
|
+
FFI.buffer_diagflat(self)
|
|
225
|
+
end
|
|
226
|
+
|
|
227
|
+
def min
|
|
228
|
+
FFI.buffer_min(self)
|
|
229
|
+
end
|
|
230
|
+
|
|
231
|
+
def max
|
|
232
|
+
FFI.buffer_max(self)
|
|
233
|
+
end
|
|
234
|
+
|
|
235
|
+
def minmax
|
|
236
|
+
return min, max
|
|
237
|
+
end
|
|
238
|
+
end
|
|
239
|
+
end
|
|
240
|
+
end
|