neuro 0.4.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.
@@ -0,0 +1,116 @@
1
+ require 'rake/clean'
2
+ require 'rake/testtask'
3
+ require 'rake/gempackagetask'
4
+ require 'rbconfig'
5
+ include Config
6
+ require 'find'
7
+ include Find
8
+
9
+ PKG_NAME = 'neuro'
10
+ PKG_VERSION = File.read('VERSION').chomp
11
+ PKG_FILES = FileList['**/*']
12
+ PKG_FILES.exclude('CVS')
13
+ PKG_FILES.exclude('pkg')
14
+ PKG_FILES.exclude(/\.dump$/)
15
+
16
+ task :default => [:test]
17
+
18
+ desc "Run unit tests"
19
+ task(:test => [:compile]) do
20
+ cd 'tests' do
21
+ ruby %{-I../ext runner.rb}
22
+ end
23
+ end
24
+
25
+ desc "Creating documentation"
26
+ task :doc do
27
+ sh 'rdoc -m Neuro -d -o doc ext/neuro.c'# lib/neuro/display.rb'
28
+ end
29
+
30
+ desc "Compiling library"
31
+ task :compile do
32
+ cd 'ext' do
33
+ ruby 'extconf.rb'
34
+ sh 'make'
35
+ end
36
+ end
37
+
38
+ desc "Installing library"
39
+ task(:install => [:test]) do
40
+ src = "ext/neuro.#{CONFIG['DLEXT']}"
41
+ filename = File.basename(src)
42
+ dst = File.join(CONFIG["sitelibdir"], filename)
43
+ install(src, dst, :verbose => true, :mode => 0644)
44
+ src = 'lib/neuro/display.rb'
45
+ filename = File.basename(src)
46
+ dst_dir = File.join(CONFIG["sitelibdir"], 'neuro')
47
+ mkdir_p dst_dir
48
+ dst = File.join(dst_dir, filename)
49
+ install(src, dst, :verbose => true, :mode => 0644)
50
+ end
51
+
52
+ task :clean do
53
+ find('.') do |f|
54
+ if f =~ /\.dump$/
55
+ rm f
56
+ end
57
+ end
58
+ cd 'ext' do
59
+ sh 'make distclean' rescue nil
60
+ end
61
+ end
62
+
63
+ spec = Gem::Specification.new do |s|
64
+
65
+ #### Basic information.
66
+
67
+ s.name = 'neuro'
68
+ s.version = PKG_VERSION
69
+ s.summary = "Neural Network Extension for Ruby"
70
+ s.description = <<EOF
71
+ A Ruby extension that provides a 2-Layer Back Propagation Neural Network, which
72
+ can be used to categorize datasets of arbitrary size.
73
+ EOF
74
+
75
+ #### Dependencies and requirements.
76
+
77
+ #s.add_dependency('log4r', '> 1.0.4')
78
+ #s.requirements << ""
79
+
80
+ s.files = PKG_FILES
81
+
82
+ #### C code extensions.
83
+
84
+ s.extensions << "ext/extconf.rb"
85
+
86
+ #### Load-time details: library and application (you will need one or both).
87
+
88
+ s.require_path = 'ext' # Use these for libraries.
89
+ s.autorequire = 'neuro'
90
+
91
+ #s.bindir = "bin" # Use these for applications.
92
+ #s.executables = ["foo.rb"]
93
+ #s.default_executable = "foo.rb"
94
+
95
+ #### Documentation and testing.
96
+
97
+ s.has_rdoc = true
98
+ s.extra_rdoc_files = [ 'ext/neuro.c' ]
99
+ s.rdoc_options << '--main' << 'Neuro'
100
+ s.test_files << 'tests/runner.rb'
101
+
102
+ #### Author and project details.
103
+
104
+ s.author = "Florian Frank"
105
+ s.email = "flori@ping.de"
106
+ s.homepage = "http://neuro.rubyforge.org"
107
+ s.rubyforge_project = "neuro"
108
+ end
109
+
110
+ Rake::GemPackageTask.new(spec) do |pkg|
111
+ pkg.package_files += PKG_FILES
112
+ pkg.need_tar = true
113
+ end
114
+
115
+ task :release => [ :clean, :compile, :package ]
116
+ # vim: set et sw=2 ts=2:
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.4.0
@@ -0,0 +1,125 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'neuro'
4
+ require 'enumerator'
5
+
6
+ class OCR
7
+ include Neuro
8
+
9
+ class Character
10
+ def initialize(char, number, vector)
11
+ @char, @number, @vector = char, number, vector
12
+ end
13
+
14
+ attr_reader :char, :number, :vector
15
+
16
+ def to_s
17
+ result = ''
18
+ @vector.each_slice(5) do |row|
19
+ row.each { |pixel| result << (pixel < 0 ? ' ' : '*') }
20
+ result << "\n"
21
+ end
22
+ result
23
+ end
24
+
25
+ def dup
26
+ self.class.new(@char.dup, @number, @vector.dup)
27
+ end
28
+ end
29
+
30
+ CHAR_BTIMAP = [
31
+ "_***__****__*****_****__*****_*****_*****_*___*_____*_____*_*___*_*_____*___*_****__*****_*****_*****_*****_*****_*****_*___*_*___*_*___*_*___*_*___*_*****_",
32
+ "*___*_*___*_*_____*___*_*_____*_____*_____*___*_____*_____*_*___*_*_____**_**_*___*_*___*_*___*_*___*_*___*_*_______*___*___*_*___*_*___*_*___*_*___*_____*_",
33
+ "*___*_*___*_*_____*___*_*_____*_____*_____*___*_____*_____*_*__*__*_____*_*_*_*___*_*___*_*___*_*___*_*___*_*_______*___*___*_*___*_*___*__*_*__*___*____*__",
34
+ "*****_****__*_____*___*_****__****__*_***_*****_____*_____*_***___*_____*___*_*___*_*___*_*****_**__*_****__*****___*___*___*_*___*_*___*___*___*****___*___",
35
+ "*___*_*___*_*_____*___*_*_____*_____*___*_*___*_____*_____*_*__*__*_____*___*_*___*_*___*_*_____*_*_*_*___*_____*___*___*___*_*___*_*_*_*__*_*______*__*____",
36
+ "*___*_*___*_*_____*___*_*_____*_____*___*_*___*_____*_____*_*___*_*_____*___*_*___*_*___*_*_____*__**_*___*_____*___*___*___*__*_*__**_**_*___*_____*_*_____",
37
+ "*___*_****__*****_****__*****_*_____*****_*___*_____*_*****_*___*_****__*___*_*___*_*****_*_____*****_*___*_*****___*____****___*___*___*_*___*******_*****_",
38
+ ]
39
+
40
+ CHARACTERS = []
41
+ ('A'..'Z').each_with_index do |char, number|
42
+ vector = []
43
+ 7.times do |j|
44
+ c = CHAR_BTIMAP[j][6 * number, 5]
45
+ vector += c.enum_for(:each_byte).map { |x| x == ?* ? 1.0 : -1.0 }
46
+ end
47
+ CHARACTERS << Character.new(char, number, vector)
48
+ end
49
+
50
+ def initialize
51
+ filename = File.basename($0) + '.dump'
52
+ if File.exist?(filename)
53
+ File.open(filename, 'rb') do |f|
54
+ @network = Network.load(f)
55
+ end
56
+ else
57
+ STDERR.puts "Wait a momemt until the network has learned enough..."
58
+ @network = Network.new(5 * 7, 70, 26)
59
+ @network.debug = STDERR
60
+ @network.debug_step = 100
61
+ max_error = 1.0E-5
62
+ eta = 0.2
63
+ max_count = CHARACTERS.size * 10
64
+ count = max_count
65
+ until count < max_error
66
+ count = 0
67
+ CHARACTERS.sort_by { rand }.each do |character|
68
+ count += @network.learn(character.vector,
69
+ make_result_vector(character.number), max_error, eta)
70
+ end
71
+ end
72
+ STDERR.print "Dumping network (learned #{@network.learned} times)... "
73
+ File.open(filename, 'wb') do |f|
74
+ @network.dump(f)
75
+ end
76
+ STDERR.puts "done!"
77
+ end
78
+ end
79
+
80
+ attr_reader :network
81
+
82
+ def make_result_vector(number)
83
+ Array.new(CHARACTERS.size) { |i| number == i ? 0.9 : 0.1 }
84
+ end
85
+
86
+ def vector_to_number(vector)
87
+ vector.enum_for(:each_with_index).max[1]
88
+ end
89
+
90
+ def vector_to_char(vector)
91
+ number = vector_to_number(vector)
92
+ CHARACTERS[number]
93
+ end
94
+
95
+ def categorize(scan_vector)
96
+ decision = @network.decide(scan_vector)
97
+ vector_to_char(decision)
98
+ end
99
+
100
+ def self.noisify(character, percentage)
101
+ char = CHARACTERS.find { |c| c.char == character }
102
+ copy = char.dup
103
+ pixels = (copy.vector.size * (percentage / 100.0)).round
104
+ pixels.times do
105
+ picked = rand(copy.vector.size)
106
+ copy.vector[picked] = copy.vector[picked] < 0.0 ? 1.0 : -1.0
107
+ end
108
+ copy
109
+ end
110
+ end
111
+
112
+ if $0 == __FILE__
113
+ ocr = OCR.new
114
+ loop do
115
+ puts "", "Input a character from 'A'-'Z': "
116
+ c = gets.chomp
117
+ c.tr!('a-z', 'A-Z')
118
+ break unless /^[A-Z]$/.match(c)
119
+ input_char = OCR.noisify(c, 5)
120
+ puts "Noisy Character:", input_char, ""
121
+ rec_char = ocr.categorize(input_char.vector)
122
+ puts "Understood '#{rec_char.char}':", rec_char
123
+ end
124
+ end
125
+ # vim: set et sw=2 ts=2:
@@ -0,0 +1,6 @@
1
+ require 'mkmf'
2
+
3
+ if CONFIG['CC'] == 'gcc'
4
+ CONFIG['CC'] = 'gcc -Wall -O2'
5
+ end
6
+ create_makefile 'neuro'
@@ -0,0 +1,694 @@
1
+ #include "ruby.h"
2
+ #include <assert.h>
3
+ #include <math.h>
4
+
5
+ #define CAST2FLOAT(obj) \
6
+ if (TYPE(obj) != T_FLOAT && rb_respond_to(obj, id_to_f)) \
7
+ obj = rb_funcall(obj, id_to_f, 0, 0); \
8
+ else \
9
+ Check_Type(obj, T_FLOAT)
10
+ #define SYM(x) ID2SYM(rb_intern(x))
11
+ #define feed \
12
+ feed2layer(network->input_size, network->hidden_size, \
13
+ network->hidden_layer, network->tmp_input); \
14
+ for (i = 0; i < network->hidden_size; i++) \
15
+ network->tmp_hidden[i] = network->hidden_layer[i]->output; \
16
+ feed2layer(network->hidden_size, network->output_size, \
17
+ network->output_layer, network->tmp_hidden)
18
+ #define DEFAULT_MAX_ITERATIONS 10000
19
+ #define DEFAULT_DEBUG_STEP 1000
20
+
21
+ static VALUE rb_mNeuro, rb_cNetwork, rb_cNeuroError;
22
+ static ID id_to_f, id_class, id_name;
23
+
24
+ /* Infrastructure */
25
+
26
+ typedef struct NodeStruct {
27
+ int number_weights;
28
+ double *weights;
29
+ double output;
30
+ } Node;
31
+
32
+ typedef struct NetworkStruct {
33
+ int input_size;
34
+ int hidden_size;
35
+ int output_size;
36
+ Node** hidden_layer;
37
+ Node** output_layer;
38
+ int learned;
39
+ int debug_step;
40
+ VALUE debug;
41
+ int max_iterations;
42
+ double *tmp_input;
43
+ double *tmp_hidden;
44
+ double *tmp_output;
45
+ } Network;
46
+
47
+ /* Node methods */
48
+
49
+ static Node *Node_create(int weights)
50
+ {
51
+ Node *node;
52
+ int i;
53
+ assert(weights > 0);
54
+ node = ALLOC(Node);
55
+ MEMZERO(node, Node, 1);
56
+ node->weights = ALLOC_N(double, weights);
57
+ node->number_weights = weights;
58
+ for (i = 0; i < weights; i++)
59
+ node->weights[i] = 0.5 - rand() / (float) RAND_MAX;
60
+ node->output = 0.0;
61
+ return node;
62
+ }
63
+
64
+ static Node *Node_from_hash(VALUE hash)
65
+ {
66
+ Node *node;
67
+ VALUE weights = rb_hash_aref(hash, SYM("weights"));
68
+ VALUE output = rb_hash_aref(hash, SYM("output"));
69
+ int i, len;
70
+ Check_Type(output, T_FLOAT);
71
+ Check_Type(weights, T_ARRAY);
72
+ len = RARRAY(weights)->len;
73
+ node = Node_create(len);
74
+ node->output = RFLOAT(output)->value;
75
+ for (i = 0; i < len; i++)
76
+ node->weights[i] = RFLOAT(rb_ary_entry(weights, i))->value;
77
+ return node;
78
+ }
79
+
80
+ static void Node_destroy(Node *node)
81
+ {
82
+ MEMZERO(node->weights, double, node->number_weights);
83
+ xfree(node->weights);
84
+ MEMZERO(node, Node, 1);
85
+ xfree(node);
86
+ }
87
+
88
+ static VALUE Node_to_hash(Node *node)
89
+ {
90
+ VALUE result = rb_hash_new(), weights = rb_ary_new2(node->number_weights);
91
+ int i;
92
+ rb_hash_aset(result, SYM("output"), rb_float_new(node->output));
93
+ for (i = 0; i < node->number_weights; i++)
94
+ rb_ary_store(weights, i, rb_float_new(node->weights[i]));
95
+ rb_hash_aset(result, SYM("weights"), weights);
96
+ return result;
97
+ }
98
+
99
+ /* Network methods */
100
+
101
+ static Network *Network_allocate()
102
+ {
103
+ Network *network = ALLOC(Network);
104
+ MEMZERO(network, Network, 1);
105
+ return network;
106
+ }
107
+
108
+ static void Network_init(Network *network, int input_size, int hidden_size,
109
+ int output_size, int learned)
110
+ {
111
+ if (input_size <= 0) rb_raise(rb_cNeuroError, "input_size <= 0");
112
+ if (hidden_size <= 0) rb_raise(rb_cNeuroError, "hidden_size <= 0");
113
+ if (output_size <= 0) rb_raise(rb_cNeuroError, "output_size <= 0");
114
+ if (learned < 0) rb_raise(rb_cNeuroError, "learned < 0");
115
+ network->input_size = input_size;
116
+ network->hidden_size = hidden_size;
117
+ network->output_size = output_size;
118
+ network->learned = learned;
119
+ network->hidden_layer = ALLOC_N(Node*, hidden_size);
120
+ network->output_layer = ALLOC_N(Node*, output_size);
121
+ network->debug = Qnil; /* Debugging switched off */
122
+ network->debug_step = DEFAULT_DEBUG_STEP;
123
+ network->max_iterations = DEFAULT_MAX_ITERATIONS;
124
+ network->tmp_input = ALLOC_N(double, input_size);
125
+ MEMZERO(network->tmp_input, double, network->input_size);
126
+ network->tmp_hidden = ALLOC_N(double, hidden_size);
127
+ MEMZERO(network->tmp_hidden, double, network->hidden_size);
128
+ network->tmp_output = ALLOC_N(double, output_size);
129
+ MEMZERO(network->tmp_output, double, network->output_size);
130
+ }
131
+
132
+ static void Network_init_weights(Network *network)
133
+ {
134
+ int i;
135
+ for (i = 0; i < network->hidden_size; i++)
136
+ network->hidden_layer[i] = Node_create(network->input_size);
137
+ for (i = 0; i < network->output_size; i++)
138
+ network->output_layer[i] = Node_create(network->hidden_size);
139
+ }
140
+
141
+ static void Network_debug_error(Network *network, int count, float error, float
142
+ max_error)
143
+ {
144
+ VALUE argv[5];
145
+ int argc = 5;
146
+ if (!NIL_P(network->debug)) {
147
+ argv[0] = rb_str_new2("%6u.\tcount = %u\terror = %e\tmax_error = %e\n");
148
+ argv[1] = INT2NUM(network->learned);
149
+ argv[2] = INT2NUM(count);
150
+ argv[3] = rb_float_new(error / 2.0);
151
+ argv[4] = rb_float_new(max_error / 2.0);
152
+ rb_io_write(network->debug, rb_f_sprintf(argc, argv));
153
+ }
154
+ }
155
+
156
+ static void Network_debug_bail_out(Network *network)
157
+ {
158
+ VALUE argv[2];
159
+ int argc = 2;
160
+ if (!NIL_P(network->debug)) {
161
+ argv[0] = rb_str_new2("Network didn't converge after %u iterations! => Bailing out!\n");
162
+ argv[1] = INT2NUM(network->max_iterations);
163
+ rb_io_write(network->debug, rb_f_sprintf(argc, argv));
164
+ }
165
+ }
166
+
167
+ static VALUE Network_to_hash(Network *network)
168
+ {
169
+ int i;
170
+ VALUE hidden_layer, output_layer, result = rb_hash_new();
171
+
172
+ rb_hash_aset(result, SYM("input_size"), INT2NUM(network->input_size));
173
+ rb_hash_aset(result, SYM("hidden_size"), INT2NUM(network->hidden_size));
174
+ rb_hash_aset(result, SYM("output_size"), INT2NUM(network->output_size));
175
+ hidden_layer = rb_ary_new2(network->hidden_size);
176
+ for (i = 0; i < network->hidden_size; i++)
177
+ rb_ary_store(hidden_layer, i, Node_to_hash(network->hidden_layer[i]));
178
+ rb_hash_aset(result, SYM("hidden_layer"), hidden_layer);
179
+ output_layer = rb_ary_new2(network->output_size);
180
+ for (i = 0; i < network->output_size; i++)
181
+ rb_ary_store(output_layer, i, Node_to_hash(network->output_layer[i]));
182
+ rb_hash_aset(result, SYM("output_layer"), output_layer);
183
+ rb_hash_aset(result, SYM("learned"), INT2NUM(network->learned));
184
+ return result;
185
+ }
186
+
187
+ /*
188
+ * Helper Functions
189
+ */
190
+
191
+ static void transform_data(double *data_vector, VALUE data)
192
+ {
193
+ int i;
194
+ VALUE current;
195
+ for (i = 0; i < RARRAY(data)->len; i++) {
196
+ current = rb_ary_entry(data, i);
197
+ CAST2FLOAT(current);
198
+ data_vector[i] = RFLOAT(current)->value;
199
+ }
200
+ }
201
+
202
+ static void feed2layer(int in_size, int out_size, Node **layer, double *data)
203
+ {
204
+ int i, j;
205
+ double sum;
206
+ for (i = 0; i < out_size; i++) {
207
+ sum = 0.0;
208
+ for (j = 0; j < in_size; j++)
209
+ sum += layer[i]->weights[j] * data[j];
210
+ layer[i]->output = 1.0 / (1.0 + exp(-sum));
211
+ /* sigmoid(sum), beta = 0.5 */
212
+ }
213
+ }
214
+
215
+ /*
216
+ * Ruby API
217
+ */
218
+
219
+ /*
220
+ * call-seq: learn(data, desired, max_error, eta)
221
+ *
222
+ * The network should respond with the Array _desired_ (size == output_size),
223
+ * if it was given the Array _data_ (size == input_size). The learning process
224
+ * ends, if the resulting error sinks below _max_error_ and convergence is
225
+ * assumed. A lower _eta_ parameter leads to slower learning, because of low
226
+ * weight changes. A too high _eta_ can lead to wildly oscillating weights, and
227
+ * result in slower learning or no learning at all. The last two parameters
228
+ * should be chosen appropriately to the problem at hand. ;)
229
+ *
230
+ * The return value is an Integer value, that denotes the number of learning
231
+ * steps, which were necessary, to learn the _data_, or _max_iterations_, if
232
+ * the _data_ couldn't be learned.
233
+ */
234
+ static VALUE rb_network_learn(VALUE self, VALUE data, VALUE desired, VALUE
235
+ max_error, VALUE eta)
236
+ {
237
+ Network *network;
238
+ double max_error_float, eta_float, error, sum,
239
+ *output_delta, *hidden_delta;
240
+ int i, j, count;
241
+
242
+ Data_Get_Struct(self, Network, network);
243
+
244
+ Check_Type(data, T_ARRAY);
245
+ if (RARRAY(data)->len != network->input_size)
246
+ rb_raise(rb_cNeuroError, "size of data != input_size");
247
+ transform_data(network->tmp_input, data);
248
+
249
+ Check_Type(desired, T_ARRAY);
250
+ if (RARRAY(desired)->len != network->output_size)
251
+ rb_raise(rb_cNeuroError, "size of desired != output_size");
252
+ transform_data(network->tmp_output, desired);
253
+ CAST2FLOAT(max_error);
254
+ max_error_float = RFLOAT(max_error)->value;
255
+ if (max_error_float <= 0) rb_raise(rb_cNeuroError, "max_error <= 0");
256
+ max_error_float *= 2.0;
257
+ CAST2FLOAT(eta);
258
+ eta_float = RFLOAT(eta)->value;
259
+ if (eta_float <= 0) rb_raise(rb_cNeuroError, "eta <= 0");
260
+
261
+ output_delta = ALLOCA_N(double, network->output_size);
262
+ hidden_delta = ALLOCA_N(double, network->hidden_size);
263
+ for(count = 0; count < network->max_iterations; count++) {
264
+ feed;
265
+
266
+ /* Compute output weight deltas and current error */
267
+ error = 0.0;
268
+ for (i = 0; i < network->output_size; i++) {
269
+ output_delta[i] = network->tmp_output[i] -
270
+ network->output_layer[i]->output;
271
+ error += output_delta[i] * output_delta[i];
272
+ output_delta[i] *= network->output_layer[i]->output *
273
+ (1.0 - network->output_layer[i]->output);
274
+ /* diff * (sigmoid' = 2 * output * beta * (1 - output)) */
275
+
276
+ }
277
+
278
+ if (count % network->debug_step == 0)
279
+ Network_debug_error(network, count, error, max_error_float);
280
+
281
+ /* Get out if error is below max_error ^ 2 */
282
+ if (error < max_error_float) goto CONVERGED;
283
+
284
+ /* Compute hidden weight deltas */
285
+
286
+ for (i = 0; i < network->hidden_size; i++) {
287
+ sum = 0.0;
288
+ for (j = 0; j < network->output_size; j++)
289
+ sum += output_delta[j] *
290
+ network->output_layer[j]->weights[i];
291
+ hidden_delta[i] = sum * network->hidden_layer[i]->output *
292
+ (1.0 - network->hidden_layer[i]->output);
293
+ /* sum * (sigmoid' = 2 * output * beta * (1 - output)) */
294
+ }
295
+
296
+ /* Adjust weights */
297
+
298
+ for (i = 0; i < network->output_size; i++)
299
+ for (j = 0; j < network->hidden_size; j++)
300
+ network->output_layer[i]->weights[j] +=
301
+ eta_float * output_delta[i] *
302
+ network->hidden_layer[j]->output;
303
+
304
+ for (i = 0; i < network->hidden_size; i++)
305
+ for (j = 0; j < network->input_size; j++)
306
+ network->hidden_layer[i]->weights[j] += eta_float *
307
+ hidden_delta[i] * network->tmp_input[j];
308
+ }
309
+ Network_debug_bail_out(network);
310
+ CONVERGED:
311
+ network->learned++;
312
+ return INT2NUM(count);
313
+ }
314
+
315
+ /*
316
+ * call-seq: decide(data)
317
+ *
318
+ * The network is given the Array _data_ (size has to be == input_size), and it
319
+ * responds with another Array (size == output_size) by returning it.
320
+ */
321
+ static VALUE rb_network_decide(VALUE self, VALUE data)
322
+ {
323
+ Network *network;
324
+ VALUE result;
325
+ int i;
326
+
327
+ Data_Get_Struct(self, Network, network);
328
+
329
+ Check_Type(data, T_ARRAY);
330
+ if (RARRAY(data)->len != network->input_size)
331
+ rb_raise(rb_cNeuroError, "size of data != input_size");
332
+ transform_data(network->tmp_input, data);
333
+ feed;
334
+ result = rb_ary_new2(network->output_size);
335
+ for (i = 0; i < network->output_size; i++) {
336
+ rb_ary_store(result, i,
337
+ rb_float_new(network->output_layer[i]->output));
338
+ }
339
+ return result;
340
+ }
341
+
342
+ /*
343
+ * Returns the _input_size_ of this Network as an Integer. This is the number
344
+ * of weights, that are connected to the input of the hidden layer.
345
+ */
346
+ static VALUE rb_network_input_size(VALUE self)
347
+ {
348
+ Network *network;
349
+
350
+ Data_Get_Struct(self, Network, network);
351
+ return INT2NUM(network->input_size);
352
+ }
353
+
354
+ /*
355
+ * Returns the _hidden_size_ of this Network as an Integer. This is the number of nodes in
356
+ * the hidden layer.
357
+ */
358
+ static VALUE rb_network_hidden_size(VALUE self)
359
+ {
360
+ Network *network;
361
+
362
+ Data_Get_Struct(self, Network, network);
363
+ return INT2NUM(network->hidden_size);
364
+ }
365
+
366
+ /*
367
+ * Returns the _output_size_ of this Network as an Integer. This is the number
368
+ * of nodes in the output layer.
369
+ */
370
+ static VALUE rb_network_output_size(VALUE self)
371
+ {
372
+ Network *network;
373
+
374
+ Data_Get_Struct(self, Network, network);
375
+ return INT2NUM(network->output_size);
376
+ }
377
+
378
+ /*
379
+ * Returns the number of calls to #learn as an integer.
380
+ */
381
+ static VALUE rb_network_learned(VALUE self)
382
+ {
383
+ Network *network;
384
+
385
+ Data_Get_Struct(self, Network, network);
386
+ return INT2NUM(network->learned);
387
+ }
388
+
389
+ /*
390
+ * Returns nil, if debugging is switchted off. Returns the IO object, that is
391
+ * used for debugging output, if debugging is switchted on.
392
+ */
393
+ static VALUE rb_network_debug(VALUE self)
394
+ {
395
+ Network *network;
396
+
397
+ Data_Get_Struct(self, Network, network);
398
+ return network->debug;
399
+ }
400
+
401
+ /*
402
+ * call-seq: debug=(io)
403
+ *
404
+ * Switches debugging on, if _io_ is an IO object. If it is nil,
405
+ * debugging is switched off.
406
+ */
407
+ static VALUE rb_network_debug_set(VALUE self, VALUE io)
408
+ {
409
+ Network *network;
410
+
411
+ Data_Get_Struct(self, Network, network);
412
+ network->debug = io;
413
+ return io;
414
+ }
415
+
416
+ /*
417
+ * Returns the Integer number of steps, that are done during learning, before a
418
+ * debugging message is printed to #debug.
419
+ */
420
+ static VALUE rb_network_debug_step(VALUE self)
421
+ {
422
+ Network *network;
423
+
424
+ Data_Get_Struct(self, Network, network);
425
+ return INT2NUM(network->debug_step);
426
+ }
427
+
428
+ /*
429
+ * call-seq: debug_step=(step)
430
+ *
431
+ * Sets the number of steps, that are done during learning, before a
432
+ * debugging message is printed to _step_. If _step_ is equal to or less than 0
433
+ * the default value (=1000) is set.
434
+ */
435
+ static VALUE rb_network_debug_step_set(VALUE self, VALUE step)
436
+ {
437
+ Network *network;
438
+
439
+ Data_Get_Struct(self, Network, network);
440
+ Check_Type(step, T_FIXNUM);
441
+ network->debug_step = NUM2INT(step);
442
+ if (network->debug_step <= 0) network->debug_step = DEFAULT_DEBUG_STEP;
443
+ return step;
444
+ }
445
+
446
+ /*
447
+ * Returns the maximal number of iterations, that are done before #learn gives
448
+ * up and returns without having learned the given _data_.
449
+ */
450
+ static VALUE rb_network_max_iterations(VALUE self)
451
+ {
452
+ Network *network;
453
+
454
+ Data_Get_Struct(self, Network, network);
455
+ return INT2NUM(network->max_iterations);
456
+ }
457
+
458
+ /*
459
+ * call-seq: max_iterations=(iterations)
460
+ *
461
+ * Sets the maximal number of iterations, that are done before #learn gives
462
+ * up and returns without having learned the given _data_, to _iterations_.
463
+ * If _iterations_ is equal to or less than 0, the default value (=10_000) is
464
+ * set.
465
+ */
466
+ static VALUE rb_network_max_iterations_set(VALUE self, VALUE iterations)
467
+ {
468
+ Network *network;
469
+
470
+ Data_Get_Struct(self, Network, network);
471
+ Check_Type(iterations, T_FIXNUM);
472
+ network->max_iterations = NUM2INT(iterations);
473
+ if (network->max_iterations <= 0)
474
+ network->max_iterations = DEFAULT_MAX_ITERATIONS;
475
+ return iterations;
476
+ }
477
+
478
+ /*
479
+ * Returns the state of the network as a Hash.
480
+ */
481
+ static VALUE rb_network_to_h(VALUE self)
482
+ {
483
+ Network *network;
484
+
485
+ Data_Get_Struct(self, Network, network);
486
+ return Network_to_hash(network);
487
+ }
488
+
489
+
490
+ /*
491
+ * Returns a short string for the network.
492
+ */
493
+ static VALUE rb_network_to_s(VALUE self)
494
+ {
495
+ Network *network;
496
+ VALUE argv[5];
497
+ int argc = 5;
498
+
499
+ Data_Get_Struct(self, Network, network);
500
+ argv[0] = rb_str_new2("#<%s:%u,%u,%u>");
501
+ argv[1] = rb_funcall(self, id_class, 0, 0);
502
+ argv[1] = rb_funcall(argv[1], id_name, 0, 0);
503
+ argv[2] = INT2NUM(network->input_size);
504
+ argv[3] = INT2NUM(network->hidden_size);
505
+ argv[4] = INT2NUM(network->output_size);
506
+ return rb_f_sprintf(argc, argv);
507
+ }
508
+
509
+ /* Allocation and Construction */
510
+
511
+ static void rb_network_mark(Network *network)
512
+ {
513
+ if (!NIL_P(network->debug)) rb_gc_mark(network->debug);
514
+ }
515
+
516
+ static void rb_network_free(Network *network)
517
+ {
518
+ int i;
519
+ for (i = 0; i < network->hidden_size; i++)
520
+ Node_destroy(network->hidden_layer[i]);
521
+ MEMZERO(network->hidden_layer, Node*, network->hidden_size);
522
+ xfree(network->hidden_layer);
523
+ for (i = 0; i < network->output_size; i++)
524
+ Node_destroy(network->output_layer[i]);
525
+ MEMZERO(network->output_layer, Node*, network->output_size);
526
+ xfree(network->output_layer);
527
+ MEMZERO(network->tmp_input, double, network->input_size);
528
+ xfree(network->tmp_input);
529
+ MEMZERO(network->tmp_hidden, double, network->hidden_size);
530
+ xfree(network->tmp_hidden);
531
+ MEMZERO(network->tmp_output, double, network->output_size);
532
+ xfree(network->tmp_output);
533
+ MEMZERO(network, Network, 1);
534
+ xfree(network);
535
+ }
536
+
537
+ static VALUE rb_network_s_allocate(VALUE klass)
538
+ {
539
+ Network *network = Network_allocate();
540
+ return Data_Wrap_Struct(klass, rb_network_mark, rb_network_free, network);
541
+ }
542
+
543
+ /*
544
+ * call-seq: new(input_size, hidden_size, output_size)
545
+ *
546
+ * Returns a Neuro::Network instance of the given size specification.
547
+ */
548
+ static VALUE rb_network_initialize(int argc, VALUE *argv, VALUE self)
549
+ {
550
+ Network *network;
551
+ VALUE input_size, hidden_size, output_size;
552
+
553
+ rb_scan_args(argc, argv, "3", &input_size, &hidden_size, &output_size);
554
+ Check_Type(input_size, T_FIXNUM);
555
+ Check_Type(hidden_size, T_FIXNUM);
556
+ Check_Type(output_size, T_FIXNUM);
557
+ Data_Get_Struct(self, Network, network);
558
+ Network_init(network, NUM2INT(input_size), NUM2INT(hidden_size),
559
+ NUM2INT(output_size), 0);
560
+ Network_init_weights(network);
561
+ return self;
562
+ }
563
+
564
+ /*
565
+ * Returns the serialized data for this Network instance for the Marshal
566
+ * module.
567
+ */
568
+ static VALUE rb_network_dump(int argc, VALUE *argv, VALUE self)
569
+ {
570
+ VALUE port = Qnil, hash;
571
+ Network *network;
572
+
573
+ rb_scan_args(argc, argv, "01", &port);
574
+ Data_Get_Struct(self, Network, network);
575
+ hash = Network_to_hash(network);
576
+ return rb_marshal_dump(hash, port);
577
+ }
578
+
579
+ static VALUE
580
+ setup_layer_i(VALUE node_hash, VALUE pair_value)
581
+ {
582
+ VALUE *pair = (VALUE *) pair_value;
583
+ Node **layer = (Node **) pair[0];
584
+ int index = (int) pair[1];
585
+ Check_Type(node_hash, T_HASH);
586
+ layer[index] = Node_from_hash(node_hash);
587
+ pair[1] = (VALUE) 1 + index;
588
+ return Qnil;
589
+ }
590
+
591
+ /*
592
+ * call-seq: Neuro::Network.load(string)
593
+ *
594
+ * Creates a Network object plus state
595
+ * from the Marshal dumped string _string_, and returns it.
596
+ */
597
+ static VALUE rb_network_load(VALUE klass, VALUE string)
598
+ {
599
+ VALUE input_size, hidden_size, output_size, learned,
600
+ hidden_layer, output_layer, pair[2];
601
+ Network *network;
602
+ VALUE hash = rb_marshal_load(string);
603
+ input_size = rb_hash_aref(hash, SYM("input_size"));
604
+ hidden_size = rb_hash_aref(hash, SYM("hidden_size"));
605
+ output_size = rb_hash_aref(hash, SYM("output_size"));
606
+ learned = rb_hash_aref(hash, SYM("learned"));
607
+ Check_Type(input_size, T_FIXNUM);
608
+ Check_Type(hidden_size, T_FIXNUM);
609
+ Check_Type(output_size, T_FIXNUM);
610
+ Check_Type(learned, T_FIXNUM);
611
+ network = Network_allocate();
612
+ Network_init(network, NUM2INT(input_size), NUM2INT(hidden_size),
613
+ NUM2INT(output_size), NUM2INT(learned));
614
+ hidden_layer = rb_hash_aref(hash, SYM("hidden_layer"));
615
+ output_layer = rb_hash_aref(hash, SYM("output_layer"));
616
+ Check_Type(hidden_layer, T_ARRAY);
617
+ Check_Type(output_layer, T_ARRAY);
618
+ pair[0] = (VALUE) network->hidden_layer;
619
+ pair[1] = (VALUE) 0;
620
+ rb_iterate(rb_each, hidden_layer, setup_layer_i, (VALUE) pair);
621
+ pair[0] = (VALUE) network->output_layer;
622
+ pair[1] = (VALUE) 0;
623
+ rb_iterate(rb_each, output_layer, setup_layer_i, (VALUE) pair);
624
+ return Data_Wrap_Struct(klass, NULL, rb_network_free, network);
625
+ }
626
+
627
+ /*
628
+ *
629
+ * = neuro - Neuronal Network Extension for Ruby
630
+ *
631
+ * == Description
632
+ *
633
+ * A Ruby extension that provides a 2-Layer Back Propagation Neural Network,
634
+ * which can be used to categorize datasets of arbitrary size.
635
+ *
636
+ * The network can be easily stored to or restored from the hard disk with
637
+ * the help of Ruby's Marshal facility.
638
+ *
639
+ * == Author
640
+ *
641
+ * Florian Frank <mailto:flori@ping.de>
642
+ *
643
+ * == License
644
+ *
645
+ * This is free software; you can redistribute it and/or modify it under
646
+ * the terms of the GNU General Public License Version 2 as published by
647
+ * the Free Software Foundation: http://www.gnu.org/copyleft/gpl.html
648
+ *
649
+ * == Download
650
+ *
651
+ * The latest version of <b>neuro</b> can be found at
652
+ *
653
+ * * http://rubyforge.org/frs/?group_id=554
654
+ *
655
+ * The homepage should be located at
656
+ *
657
+ * * http://neuro.rubyforge.org
658
+ *
659
+ * == Examples
660
+ *
661
+ * An example for optical character recognition can be found in the examples
662
+ * subdirectory. Don't forget to check out the tests subdirectory, which
663
+ * contains some additional examples.
664
+ */
665
+ void Init_neuro()
666
+ {
667
+ rb_mNeuro = rb_define_module("Neuro");
668
+ rb_cNetwork = rb_define_class_under(rb_mNeuro, "Network", rb_cObject);
669
+ rb_cNeuroError = rb_define_class("NetworkError", rb_eStandardError);
670
+ rb_define_alloc_func(rb_cNetwork, rb_network_s_allocate);
671
+ rb_define_method(rb_cNetwork, "initialize", rb_network_initialize, -1);
672
+ rb_define_method(rb_cNetwork, "learn", rb_network_learn, 4);
673
+ rb_define_method(rb_cNetwork, "decide", rb_network_decide, 1);
674
+ rb_define_method(rb_cNetwork, "input_size", rb_network_input_size, 0);
675
+ rb_define_method(rb_cNetwork, "hidden_size", rb_network_hidden_size, 0);
676
+ rb_define_method(rb_cNetwork, "output_size", rb_network_output_size, 0);
677
+ rb_define_method(rb_cNetwork, "learned", rb_network_learned, 0);
678
+ rb_define_method(rb_cNetwork, "debug", rb_network_debug, 0);
679
+ rb_define_method(rb_cNetwork, "debug=", rb_network_debug_set, 1);
680
+ rb_define_method(rb_cNetwork, "debug_step", rb_network_debug_step, 0);
681
+ rb_define_method(rb_cNetwork, "debug_step=", rb_network_debug_step_set, 1);
682
+ rb_define_method(rb_cNetwork, "max_iterations", rb_network_max_iterations, 0);
683
+ rb_define_method(rb_cNetwork, "max_iterations=", rb_network_max_iterations_set, 1);
684
+ rb_define_method(rb_cNetwork, "_dump", rb_network_dump, -1);
685
+ rb_define_method(rb_cNetwork, "dump", rb_network_dump, -1);
686
+ rb_define_method(rb_cNetwork, "to_h", rb_network_to_h, 0);
687
+ rb_define_method(rb_cNetwork, "to_s", rb_network_to_s, 0);
688
+ rb_define_singleton_method(rb_cNetwork, "_load", rb_network_load, 1);
689
+ rb_define_singleton_method(rb_cNetwork, "load", rb_network_load, 1);
690
+ id_to_f = rb_intern("to_f");
691
+ id_class = rb_intern("class");
692
+ id_name = rb_intern("name");
693
+ }
694
+ /* vim: set cin sw=4 ts=4: */