CooCoo 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- 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,168 @@
|
|
1
|
+
require 'coo-coo/consts'
|
2
|
+
require 'coo-coo/math'
|
3
|
+
require 'coo-coo/debug'
|
4
|
+
require 'coo-coo/layer_factory'
|
5
|
+
require 'coo-coo/neuron'
|
6
|
+
require 'coo-coo/sequence'
|
7
|
+
require 'coo-coo/weight_deltas'
|
8
|
+
|
9
|
+
module CooCoo
|
10
|
+
class NeuronLayer
|
11
|
+
LayerFactory.register_type(self)
|
12
|
+
|
13
|
+
attr_accessor :activation_function
|
14
|
+
|
15
|
+
def initialize(num_inputs, size, activation_function = CooCoo.default_activation)
|
16
|
+
@activation_function = activation_function
|
17
|
+
@neurons = Array.new
|
18
|
+
size.times do |i|
|
19
|
+
@neurons[i] = Neuron.new(num_inputs, activation_function)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def neurons
|
24
|
+
@neurons
|
25
|
+
end
|
26
|
+
|
27
|
+
def num_inputs
|
28
|
+
@neurons[0].num_inputs
|
29
|
+
end
|
30
|
+
|
31
|
+
def size
|
32
|
+
@neurons.size
|
33
|
+
end
|
34
|
+
|
35
|
+
def forward(input, hidden_state)
|
36
|
+
o = @neurons.each_with_index.inject(CooCoo::Vector.zeros(size)) do |acc, (neuron, i)|
|
37
|
+
acc[i] = neuron.forward(input)
|
38
|
+
acc
|
39
|
+
end
|
40
|
+
|
41
|
+
return o, hidden_state
|
42
|
+
end
|
43
|
+
|
44
|
+
def backprop(input, output, errors, hidden_state)
|
45
|
+
o = @neurons.each_with_index.inject(CooCoo::Vector.zeros(size)) do |acc, (n, i)|
|
46
|
+
acc[i] = n.backprop(input, output[i], errors[i])
|
47
|
+
acc
|
48
|
+
end
|
49
|
+
|
50
|
+
return o, hidden_state
|
51
|
+
end
|
52
|
+
|
53
|
+
def transfer_error(deltas)
|
54
|
+
@neurons.each_with_index.inject(CooCoo::Vector.zeros(num_inputs)) do |acc, (n, i)|
|
55
|
+
acc + n.transfer_error(deltas[i])
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
def transfer_input_error(expecting)
|
60
|
+
(output - expecting).to_a
|
61
|
+
end
|
62
|
+
|
63
|
+
def update_weights!(inputs, deltas)
|
64
|
+
adjust_weights!(weight_deltas(inputs, deltas))
|
65
|
+
end
|
66
|
+
|
67
|
+
def adjust_weights!(deltas)
|
68
|
+
@neurons.each_with_index do |n, i|
|
69
|
+
n.adjust_weights!(deltas.bias_deltas[i], deltas.weight_deltas[i])
|
70
|
+
end
|
71
|
+
|
72
|
+
self
|
73
|
+
end
|
74
|
+
|
75
|
+
def weight_deltas(inputs, deltas)
|
76
|
+
WeightDeltas.new(*@neurons.each_with_index.inject([ CooCoo::Vector.zeros(size), CooCoo::Sequence.new(size) ]) do |acc, (n, i)|
|
77
|
+
acc[0][i], acc[1][i] = n.weight_deltas(inputs, deltas[i])
|
78
|
+
acc
|
79
|
+
end)
|
80
|
+
end
|
81
|
+
|
82
|
+
def to_hash(network = nil)
|
83
|
+
{ type: self.class.to_s,
|
84
|
+
outputs: @neurons.size,
|
85
|
+
neurons: @neurons.collect(&:to_hash)
|
86
|
+
}
|
87
|
+
end
|
88
|
+
|
89
|
+
def resize!(new_size)
|
90
|
+
n = @neurons + Array.new(new_size - @neurons.size)
|
91
|
+
(@neurons.size...new_size).each do |i|
|
92
|
+
n[i] = Neuron.new(num_inputs)
|
93
|
+
end
|
94
|
+
|
95
|
+
@neurons = n
|
96
|
+
|
97
|
+
self
|
98
|
+
end
|
99
|
+
|
100
|
+
def update_neuron_from_hash!(neuron_index, h)
|
101
|
+
if neuron_index > @neurons.size
|
102
|
+
resize!(neuron_index)
|
103
|
+
end
|
104
|
+
|
105
|
+
@neurons[neuron_index].update_from_hash!(h)
|
106
|
+
end
|
107
|
+
|
108
|
+
def update_from_hash!(h)
|
109
|
+
resize!(h[:outputs])
|
110
|
+
|
111
|
+
h[:outputs].times do |i|
|
112
|
+
update_neuron_from_hash!(i, h[:neurons][i])
|
113
|
+
end
|
114
|
+
|
115
|
+
self
|
116
|
+
end
|
117
|
+
|
118
|
+
def ==(other)
|
119
|
+
other.kind_of?(self.class) &&
|
120
|
+
size == other.size &&
|
121
|
+
neurons.zip(other.neurons).all? { |a, b| a == b }
|
122
|
+
end
|
123
|
+
|
124
|
+
class << self
|
125
|
+
def from_hash(h, network = nil)
|
126
|
+
self.new(h[:neurons].size, h[:outputs]).update_from_hash!(h)
|
127
|
+
end
|
128
|
+
end
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
132
|
+
if __FILE__ == $0
|
133
|
+
layer = CooCoo::NeuronLayer.new(4, 2, CooCoo::ActivationFunctions.from_name(ENV.fetch("ACTIVATION", "Logistic")))
|
134
|
+
inputs = [ [ 1.0, 0.0, 0.0, 0.0 ],
|
135
|
+
[ 0.0, 0.0, 1.0, 0.0 ],
|
136
|
+
[ 0.0, 1.0, 0.0, 0.0],
|
137
|
+
[ 0.0, 0.0, 0.0, 1.0 ]
|
138
|
+
].collect do |v|
|
139
|
+
CooCoo::Vector[v]
|
140
|
+
end
|
141
|
+
targets = [ [ 1.0, 0.0 ],
|
142
|
+
[ 0.0, 1.0 ],
|
143
|
+
[ 0.0, 0.0 ],
|
144
|
+
[ 0.0, 0.0 ]
|
145
|
+
].collect do |v|
|
146
|
+
CooCoo::Vector[v]
|
147
|
+
end
|
148
|
+
|
149
|
+
inputs.zip(targets).cycle(ENV.fetch('LOOPS', 100).to_i).each do |(input, target)|
|
150
|
+
output, hidden_state = layer.forward(input, Hash.new)
|
151
|
+
puts("#{input} -> #{target}")
|
152
|
+
puts("\toutput: #{output}")
|
153
|
+
|
154
|
+
err = (output - target)
|
155
|
+
#err = err * err * 0.5
|
156
|
+
delta, hidden_state = layer.backprop(input, output, err, hidden_state)
|
157
|
+
puts("\tdelta: #{delta}")
|
158
|
+
puts("\terror: #{err}")
|
159
|
+
puts("\txfer: #{layer.transfer_error(delta)}")
|
160
|
+
|
161
|
+
layer.update_weights!(input, delta * 0.5)
|
162
|
+
end
|
163
|
+
|
164
|
+
inputs.zip(targets).each do |(input, target)|
|
165
|
+
output, hidden_state = layer.forward(input, Hash.new)
|
166
|
+
puts("#{input} -> #{output}\t#{target}")
|
167
|
+
end
|
168
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
require 'optparse'
|
2
|
+
|
3
|
+
module CooCoo
|
4
|
+
class OptionParser < ::OptionParser
|
5
|
+
def parse!(argv)
|
6
|
+
left_overs = []
|
7
|
+
begin
|
8
|
+
left_overs += super(argv)
|
9
|
+
rescue OptionParser::InvalidOption
|
10
|
+
left_overs += $!.args
|
11
|
+
left_overs << argv.shift
|
12
|
+
retry
|
13
|
+
end
|
14
|
+
|
15
|
+
left_overs
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module CooCoo
|
2
|
+
module Platform
|
3
|
+
def self.windows?
|
4
|
+
RbConfig::CONFIG['PLATFORM_DIR'] == 'win32'
|
5
|
+
end
|
6
|
+
|
7
|
+
ROOT = File.dirname(File.dirname(File.dirname(__FILE__)))
|
8
|
+
def self.root
|
9
|
+
ROOT
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
def self.root
|
14
|
+
Platform.root
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
@@ -0,0 +1,99 @@
|
|
1
|
+
require 'coo-coo/math'
|
2
|
+
require 'coo-coo/layer_factory'
|
3
|
+
|
4
|
+
module CooCoo
|
5
|
+
module Recurrence
|
6
|
+
class Backend
|
7
|
+
LayerFactory.register_type(self)
|
8
|
+
|
9
|
+
attr_reader :recurrence_layer
|
10
|
+
|
11
|
+
def initialize(recurrence_layer, outputs, recurrent_outputs)
|
12
|
+
@recurrence_layer = recurrence_layer
|
13
|
+
@outputs = outputs
|
14
|
+
@recurrent_size = recurrent_outputs
|
15
|
+
end
|
16
|
+
|
17
|
+
def num_inputs
|
18
|
+
size + recurrent_size
|
19
|
+
end
|
20
|
+
|
21
|
+
def size
|
22
|
+
@outputs
|
23
|
+
end
|
24
|
+
|
25
|
+
def recurrent_size
|
26
|
+
@recurrent_size
|
27
|
+
end
|
28
|
+
|
29
|
+
def activation_function
|
30
|
+
nil
|
31
|
+
end
|
32
|
+
|
33
|
+
def forward(inputs, hidden_state)
|
34
|
+
hidden_state ||= Hash.new
|
35
|
+
hidden_state[self] ||= Array.new
|
36
|
+
hidden_state[self].push(inputs[size, recurrent_size])
|
37
|
+
|
38
|
+
return inputs[0, size], hidden_state
|
39
|
+
end
|
40
|
+
|
41
|
+
def backprop(input, output, errors, hidden_state)
|
42
|
+
layer_state = hidden_state[@recurrence_layer]
|
43
|
+
rec_errors = (layer_state && layer_state.pop) || CooCoo::Vector.zeros(recurrent_size)
|
44
|
+
return errors.append(rec_errors), hidden_state
|
45
|
+
end
|
46
|
+
|
47
|
+
def transfer_error(deltas)
|
48
|
+
deltas
|
49
|
+
end
|
50
|
+
|
51
|
+
def update_weights!(inputs, deltas)
|
52
|
+
self
|
53
|
+
end
|
54
|
+
|
55
|
+
def adjust_weights!(deltas)
|
56
|
+
self
|
57
|
+
end
|
58
|
+
|
59
|
+
def weight_deltas(inputs, deltas)
|
60
|
+
inputs * deltas
|
61
|
+
end
|
62
|
+
|
63
|
+
def to_hash(network = nil)
|
64
|
+
{ type: self.class.name,
|
65
|
+
outputs: @outputs,
|
66
|
+
recurrent_size: @recurrent_size,
|
67
|
+
recurrence_layer: network && network.layer_index(@recurrence_layer)
|
68
|
+
}
|
69
|
+
end
|
70
|
+
|
71
|
+
def ==(other)
|
72
|
+
other.kind_of?(self.class) &&
|
73
|
+
size = other.size &&
|
74
|
+
recurrence_layer == other.recurrence_layer &&
|
75
|
+
recurrent_size == other.recurrent_size
|
76
|
+
end
|
77
|
+
|
78
|
+
def update_from_hash!(h)
|
79
|
+
@outputs = h.fetch(:outputs)
|
80
|
+
@recurrent_size = h.fetch(:recurrent_size)
|
81
|
+
self
|
82
|
+
end
|
83
|
+
|
84
|
+
def self.from_hash(h, network)
|
85
|
+
frontend = network.layers[h.fetch(:recurrence_layer)]
|
86
|
+
raise ArgumentError.new("Frontend not found") unless frontend
|
87
|
+
|
88
|
+
layer = self.new(frontend,
|
89
|
+
h.fetch(:outputs),
|
90
|
+
h.fetch(:recurrent_size)).
|
91
|
+
update_from_hash!(h)
|
92
|
+
|
93
|
+
frontend.backend = layer
|
94
|
+
|
95
|
+
layer
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
@@ -0,0 +1,101 @@
|
|
1
|
+
require 'coo-coo/math'
|
2
|
+
require 'coo-coo/layer_factory'
|
3
|
+
require 'coo-coo/recurrence/backend'
|
4
|
+
|
5
|
+
module CooCoo
|
6
|
+
module Recurrence
|
7
|
+
class Frontend
|
8
|
+
LayerFactory.register_type(self)
|
9
|
+
|
10
|
+
def initialize(num_inputs, num_recurrent_outputs)
|
11
|
+
@num_inputs = num_inputs
|
12
|
+
@num_recurrent_outputs = num_recurrent_outputs
|
13
|
+
end
|
14
|
+
|
15
|
+
def num_inputs
|
16
|
+
@num_inputs
|
17
|
+
end
|
18
|
+
|
19
|
+
def activation_function
|
20
|
+
nil
|
21
|
+
end
|
22
|
+
|
23
|
+
def size
|
24
|
+
@num_inputs + recurrent_size
|
25
|
+
end
|
26
|
+
|
27
|
+
def recurrent_size
|
28
|
+
@num_recurrent_outputs
|
29
|
+
end
|
30
|
+
|
31
|
+
def backend
|
32
|
+
@backend ||= Backend.new(self, @num_inputs, recurrent_size)
|
33
|
+
end
|
34
|
+
|
35
|
+
def backend=(layer)
|
36
|
+
@backend = layer
|
37
|
+
end
|
38
|
+
|
39
|
+
def forward(inputs, hidden_state)
|
40
|
+
layer_state = hidden_state[@backend]
|
41
|
+
recurrent_input = layer_state && layer_state.pop
|
42
|
+
return inputs.append(recurrent_input || empty_input), hidden_state
|
43
|
+
end
|
44
|
+
|
45
|
+
def backprop(input, outputs, errors, hidden_state)
|
46
|
+
# split for real and recurrent errors
|
47
|
+
norm_errors = errors[0, num_inputs]
|
48
|
+
recurrent_errors = errors[num_inputs, recurrent_size]
|
49
|
+
|
50
|
+
# buffer the recurrent output
|
51
|
+
hidden_state ||= Hash.new
|
52
|
+
hidden_state[self] ||= Array.new
|
53
|
+
hidden_state[self].push(recurrent_errors)
|
54
|
+
|
55
|
+
# return real errors
|
56
|
+
return norm_errors, hidden_state
|
57
|
+
end
|
58
|
+
|
59
|
+
def transfer_error(deltas)
|
60
|
+
deltas
|
61
|
+
end
|
62
|
+
|
63
|
+
def weight_deltas(inputs, deltas)
|
64
|
+
inputs * deltas
|
65
|
+
end
|
66
|
+
|
67
|
+
def adjust_weights!(deltas)
|
68
|
+
self
|
69
|
+
end
|
70
|
+
|
71
|
+
def ==(other)
|
72
|
+
other.kind_of?(self.class) &&
|
73
|
+
num_inputs == other.num_inputs &&
|
74
|
+
recurrent_size == other.recurrent_size
|
75
|
+
end
|
76
|
+
|
77
|
+
def to_hash(network = nil)
|
78
|
+
{ type: self.class.name,
|
79
|
+
inputs: @num_inputs,
|
80
|
+
recurrent_outputs: @num_recurrent_outputs
|
81
|
+
}
|
82
|
+
end
|
83
|
+
|
84
|
+
def update_from_hash!(h)
|
85
|
+
@num_inputs = h.fetch(:inputs)
|
86
|
+
@num_recurrent_outputs = h.fetch(:recurrent_outputs)
|
87
|
+
|
88
|
+
self
|
89
|
+
end
|
90
|
+
|
91
|
+
def self.from_hash(h, network = nil)
|
92
|
+
self.new(h.fetch(:inputs), h.fetch(:recurrent_outputs)).update_from_hash!(h)
|
93
|
+
end
|
94
|
+
|
95
|
+
private
|
96
|
+
def empty_input
|
97
|
+
CooCoo::Vector.zeros(recurrent_size)
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
@@ -0,0 +1,187 @@
|
|
1
|
+
require 'coo-coo/math'
|
2
|
+
|
3
|
+
module CooCoo
|
4
|
+
class Sequence < Math::AbstractVector
|
5
|
+
def initialize(length, &init)
|
6
|
+
if length.kind_of?(Array)
|
7
|
+
@elements = length
|
8
|
+
else
|
9
|
+
@elements = Array.new(length, &init)
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
def self.[](value, max_size = nil)
|
14
|
+
self.new(value.to_a)
|
15
|
+
# ret = new(max_size || value.size)
|
16
|
+
# value.each_with_index do |v, i|
|
17
|
+
# ret[i] = v
|
18
|
+
# end
|
19
|
+
# ret
|
20
|
+
end
|
21
|
+
|
22
|
+
# def coerce(other)
|
23
|
+
# if other.respond_to?(:each)
|
24
|
+
# return self.class[other], self
|
25
|
+
# else
|
26
|
+
# return self.class.new(self.size, other), self
|
27
|
+
# end
|
28
|
+
# end
|
29
|
+
|
30
|
+
def to_a
|
31
|
+
@elements
|
32
|
+
end
|
33
|
+
|
34
|
+
def to_s
|
35
|
+
values = each.collect do |e|
|
36
|
+
e.to_s
|
37
|
+
end
|
38
|
+
|
39
|
+
"[#{values.join(', ')}]"
|
40
|
+
end
|
41
|
+
|
42
|
+
def [](i, len = nil)
|
43
|
+
v = @elements[i, len || 1]
|
44
|
+
if len
|
45
|
+
self.class[v]
|
46
|
+
else
|
47
|
+
v[0]
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
def []=(i, v)
|
52
|
+
@elements[i] = v
|
53
|
+
self
|
54
|
+
end
|
55
|
+
|
56
|
+
def each(&block)
|
57
|
+
@elements.each(&block)
|
58
|
+
end
|
59
|
+
|
60
|
+
def each_with_index(&block)
|
61
|
+
each.each_with_index(&block)
|
62
|
+
end
|
63
|
+
|
64
|
+
include Enumerable
|
65
|
+
|
66
|
+
def collect(&block)
|
67
|
+
self.class[super]
|
68
|
+
end
|
69
|
+
|
70
|
+
def reverse
|
71
|
+
self.class[@elements.reverse]
|
72
|
+
end
|
73
|
+
|
74
|
+
def last(*args)
|
75
|
+
@elements.last(*args)
|
76
|
+
end
|
77
|
+
|
78
|
+
def append(other)
|
79
|
+
v = self.class.new(size + other.size)
|
80
|
+
each_with_index do |e, i|
|
81
|
+
v[i] = e
|
82
|
+
end
|
83
|
+
other.each_with_index do |e, i|
|
84
|
+
v[i + size] = e
|
85
|
+
end
|
86
|
+
v
|
87
|
+
end
|
88
|
+
|
89
|
+
def zero
|
90
|
+
self.class.new(size) do |i|
|
91
|
+
self[0].zero
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
def sum
|
96
|
+
@elements.drop(1).inject(@elements[0]) do |acc, e|
|
97
|
+
acc + e
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
def average
|
102
|
+
sum / size.to_f
|
103
|
+
end
|
104
|
+
|
105
|
+
def sqrt
|
106
|
+
self.class[@elements.collect(&:sqrt)]
|
107
|
+
end
|
108
|
+
|
109
|
+
def -@
|
110
|
+
self.class[@elements.collect(&:-@)]
|
111
|
+
end
|
112
|
+
|
113
|
+
def +(other)
|
114
|
+
v = if other.respond_to?(:each)
|
115
|
+
raise ArgumentError.new("Size mismatch: #{size} != #{other.size}") if size != other.size
|
116
|
+
other.each.zip(each).collect do |oe, se|
|
117
|
+
se + oe
|
118
|
+
end
|
119
|
+
else
|
120
|
+
each.collect do |e|
|
121
|
+
e + other
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
self.class[v]
|
126
|
+
end
|
127
|
+
|
128
|
+
def -(other)
|
129
|
+
v = if other.respond_to?(:each)
|
130
|
+
raise ArgumentError.new("Size mismatch #{size} != #{other.size}") if size != other.size
|
131
|
+
other.each.zip(each).collect do |oe, se|
|
132
|
+
se - oe
|
133
|
+
end
|
134
|
+
else
|
135
|
+
each.collect do |e|
|
136
|
+
e - other
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
self.class[v]
|
141
|
+
end
|
142
|
+
|
143
|
+
def size
|
144
|
+
@elements.size
|
145
|
+
end
|
146
|
+
|
147
|
+
def length
|
148
|
+
@elements.size
|
149
|
+
end
|
150
|
+
|
151
|
+
def *(other)
|
152
|
+
v = if other.respond_to?(:each)
|
153
|
+
raise ArgumentError.new("Size mismatch: #{size} != #{other.size}") if size != other.size
|
154
|
+
other.each.zip(each).collect do |oe, se|
|
155
|
+
se * oe
|
156
|
+
end
|
157
|
+
else
|
158
|
+
each.collect do |e|
|
159
|
+
e * other
|
160
|
+
end
|
161
|
+
end
|
162
|
+
|
163
|
+
self.class[v]
|
164
|
+
end
|
165
|
+
|
166
|
+
def /(other)
|
167
|
+
v = if other.respond_to?(:each)
|
168
|
+
raise ArgumentError.new("Size mismatch: #{size} != #{other.size}") if size != other.size
|
169
|
+
other.each.zip(each).collect do |oe, se|
|
170
|
+
se / oe
|
171
|
+
end
|
172
|
+
else
|
173
|
+
each.collect do |e|
|
174
|
+
e / other
|
175
|
+
end
|
176
|
+
end
|
177
|
+
|
178
|
+
self.class[v]
|
179
|
+
end
|
180
|
+
|
181
|
+
def ==(other)
|
182
|
+
other.size == size && each.zip(other.each).all? do |a, b|
|
183
|
+
a == b
|
184
|
+
end
|
185
|
+
end
|
186
|
+
end
|
187
|
+
end
|