som 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.document +5 -0
- data/.gitignore +21 -0
- data/LICENSE +20 -0
- data/README.rdoc +24 -0
- data/Rakefile +45 -0
- data/VERSION +1 -0
- data/examples/example.rb +10 -0
- data/lib/som/node.rb +42 -0
- data/lib/som.rb +107 -0
- data/som.gemspec +60 -0
- data/spec/node_spec.rb +75 -0
- data/spec/som_spec.rb +82 -0
- data/spec/spec.opts +1 -0
- data/spec/spec_helper.rb +9 -0
- metadata +81 -0
data/.document
ADDED
data/.gitignore
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2009 reddavis
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.rdoc
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
= SOM - Self Organising Map
|
2
|
+
|
3
|
+
A pure Ruby implementation of the Self Organising Map machine learning Algorithm.
|
4
|
+
|
5
|
+
== Install
|
6
|
+
|
7
|
+
gem sources -a -http://gemcutter.org
|
8
|
+
sudo gem install som
|
9
|
+
|
10
|
+
== How To Use
|
11
|
+
|
12
|
+
require 'rubygems'
|
13
|
+
require 'som'
|
14
|
+
|
15
|
+
a = SOM.new(:number_of_nodes => 4, :dimensions => 3)
|
16
|
+
a.train(data)
|
17
|
+
|
18
|
+
# Returns the index of the data you gave it
|
19
|
+
a.inspect
|
20
|
+
#=> [[1, 0...], [99, 84...], [11, 23...], [2, 6...]]
|
21
|
+
|
22
|
+
== Copyright
|
23
|
+
|
24
|
+
Copyright (c) 2009 Red Davis. See LICENSE for details.
|
data/Rakefile
ADDED
@@ -0,0 +1,45 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'rake'
|
3
|
+
|
4
|
+
begin
|
5
|
+
require 'jeweler'
|
6
|
+
Jeweler::Tasks.new do |gem|
|
7
|
+
gem.name = "som"
|
8
|
+
gem.summary = %Q{A Self Organising Map}
|
9
|
+
gem.description = %Q{A Self Organising Map}
|
10
|
+
gem.email = "reddavis@gmail.com"
|
11
|
+
gem.homepage = "http://github.com/reddavis/som"
|
12
|
+
gem.authors = ["reddavis"]
|
13
|
+
gem.add_development_dependency "rspec", ">= 1.2.9"
|
14
|
+
# gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
|
15
|
+
end
|
16
|
+
Jeweler::GemcutterTasks.new
|
17
|
+
rescue LoadError
|
18
|
+
puts "Jeweler (or a dependency) not available. Install it with: sudo gem install jeweler"
|
19
|
+
end
|
20
|
+
|
21
|
+
require 'spec/rake/spectask'
|
22
|
+
Spec::Rake::SpecTask.new(:spec) do |spec|
|
23
|
+
spec.libs << 'lib' << 'spec'
|
24
|
+
spec.spec_files = FileList['spec/**/*_spec.rb']
|
25
|
+
end
|
26
|
+
|
27
|
+
Spec::Rake::SpecTask.new(:rcov) do |spec|
|
28
|
+
spec.libs << 'lib' << 'spec'
|
29
|
+
spec.pattern = 'spec/**/*_spec.rb'
|
30
|
+
spec.rcov = true
|
31
|
+
end
|
32
|
+
|
33
|
+
task :spec => :check_dependencies
|
34
|
+
|
35
|
+
task :default => :spec
|
36
|
+
|
37
|
+
require 'rake/rdoctask'
|
38
|
+
Rake::RDocTask.new do |rdoc|
|
39
|
+
version = File.exist?('VERSION') ? File.read('VERSION') : ""
|
40
|
+
|
41
|
+
rdoc.rdoc_dir = 'rdoc'
|
42
|
+
rdoc.title = "som #{version}"
|
43
|
+
rdoc.rdoc_files.include('README*')
|
44
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
45
|
+
end
|
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
0.0.1
|
data/examples/example.rb
ADDED
@@ -0,0 +1,10 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/../lib/som')
|
2
|
+
|
3
|
+
data = Array.new(100) {Array.new(3) {rand}}
|
4
|
+
|
5
|
+
a = SOM.new(:number_of_nodes => 4, :dimensions => 3)
|
6
|
+
|
7
|
+
a.train(data)
|
8
|
+
|
9
|
+
# Returns the index of the data you gave it
|
10
|
+
puts a.inspect.inspect
|
data/lib/som/node.rb
ADDED
@@ -0,0 +1,42 @@
|
|
1
|
+
class Node
|
2
|
+
|
3
|
+
attr_reader :bucket
|
4
|
+
|
5
|
+
def initialize(number_of_weights)
|
6
|
+
create_weights(number_of_weights)
|
7
|
+
@bucket = []
|
8
|
+
end
|
9
|
+
|
10
|
+
def weights
|
11
|
+
@weights ||= []
|
12
|
+
end
|
13
|
+
|
14
|
+
def update_weight(learning_rate, inputs, neighborhood_function=1)
|
15
|
+
weights.each_with_index do |weight, index|
|
16
|
+
@weights[index] += learning_rate * neighborhood_function * (inputs[index] - weight)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
# A bucket is a place to put the data that is closest to it
|
21
|
+
def <<(data)
|
22
|
+
@bucket << data
|
23
|
+
end
|
24
|
+
|
25
|
+
# Euclidean Distance
|
26
|
+
def distance_from(data_points)
|
27
|
+
distance = 0
|
28
|
+
data_points.each_with_index do |point, index|
|
29
|
+
distance += (point - weights[index]) ** 2
|
30
|
+
end
|
31
|
+
Math.sqrt(distance)
|
32
|
+
end
|
33
|
+
|
34
|
+
private
|
35
|
+
|
36
|
+
def create_weights(number_of_weights)
|
37
|
+
number_of_weights.times do
|
38
|
+
weights << (rand > 0.5 ? -rand : rand)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
end
|
data/lib/som.rb
ADDED
@@ -0,0 +1,107 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/som/node')
|
2
|
+
|
3
|
+
class SOM
|
4
|
+
|
5
|
+
def initialize(options={})
|
6
|
+
@number_of_nodes = options[:nodes] || 5
|
7
|
+
@dimensions = options[:dimensions]
|
8
|
+
@learning_rate = options[:learning_rate] || 0.5
|
9
|
+
@radius = options[:radius] || @number_of_nodes / 2
|
10
|
+
@iteration_count = 0
|
11
|
+
@max_iterations = options[:max_iterations] || 500
|
12
|
+
# TODO: Allow a lambda so we can use different neighborhood functions
|
13
|
+
@neighborhood_function = options[:neighborhood_function] || 1
|
14
|
+
create_nodes
|
15
|
+
end
|
16
|
+
|
17
|
+
def nodes
|
18
|
+
@nodes ||= []
|
19
|
+
end
|
20
|
+
|
21
|
+
def train(data)
|
22
|
+
while train_it!(data)
|
23
|
+
end
|
24
|
+
# Place the data in the nodes buckets so we can see how
|
25
|
+
# The data has been clustered
|
26
|
+
place_data_into_buckets(data)
|
27
|
+
end
|
28
|
+
|
29
|
+
# Returns an array of buckets containing the index of the data given
|
30
|
+
def inspect
|
31
|
+
nodes.map {|x| x.bucket.map {|x| x[0]}}
|
32
|
+
end
|
33
|
+
|
34
|
+
# Return data from node that is closest to data
|
35
|
+
# You are returned a bucket which contains arrays that look like:
|
36
|
+
# [index, [data]]
|
37
|
+
# The index is the original index of that that was pumped into the classifier
|
38
|
+
# during the training process
|
39
|
+
def classify(data)
|
40
|
+
closest_node = find_closest_node(data)
|
41
|
+
closest_node.bucket
|
42
|
+
end
|
43
|
+
|
44
|
+
private
|
45
|
+
|
46
|
+
def train_it!(data)
|
47
|
+
return false if @iteration_count >= @max_iterations
|
48
|
+
|
49
|
+
data.each do |input|
|
50
|
+
# Update closest node
|
51
|
+
closest_node = find_closest_node(input)
|
52
|
+
closest_node.update_weight(@learning_rate, input)
|
53
|
+
|
54
|
+
# Update nodes that closer than the radius
|
55
|
+
other_nodes = nodes - [closest_node]
|
56
|
+
other_nodes.each do |node|
|
57
|
+
next if @radius > node.distance_from(closest_node.weights)
|
58
|
+
|
59
|
+
node.update_weight(@learning_rate, input, neighborhood_function)
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
decrease_radius!
|
64
|
+
decrease_learning_rate!
|
65
|
+
increase_iteration_count!
|
66
|
+
end
|
67
|
+
|
68
|
+
def place_data_into_buckets(data)
|
69
|
+
data.each_with_index do |input, index|
|
70
|
+
closest_node = find_closest_node(input)
|
71
|
+
closest_node << [index, input]
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
def decrease_radius!
|
76
|
+
@radius = 0.5 * @radius * @iteration_count / @max_iterations
|
77
|
+
end
|
78
|
+
|
79
|
+
def decrease_learning_rate!
|
80
|
+
@learning_rate = 0.5 * @learning_rate * @iteration_count / @max_iterations
|
81
|
+
end
|
82
|
+
|
83
|
+
def increase_iteration_count!
|
84
|
+
@iteration_count += 1
|
85
|
+
end
|
86
|
+
|
87
|
+
def neighborhood_function
|
88
|
+
0.5 * @neighborhood_function * @iteration_count / @max_iterations
|
89
|
+
end
|
90
|
+
|
91
|
+
def find_closest_node(data)
|
92
|
+
closest_node = [nodes[0], nodes[0].distance_from(data)]
|
93
|
+
|
94
|
+
nodes[1..-1].each do |node|
|
95
|
+
distance = node.distance_from(data)
|
96
|
+
if distance < closest_node[1]
|
97
|
+
closest_node = [node, distance]
|
98
|
+
end
|
99
|
+
end
|
100
|
+
closest_node[0]
|
101
|
+
end
|
102
|
+
|
103
|
+
def create_nodes
|
104
|
+
@number_of_nodes.times { nodes << Node.new(@dimensions) }
|
105
|
+
end
|
106
|
+
|
107
|
+
end
|
data/som.gemspec
ADDED
@@ -0,0 +1,60 @@
|
|
1
|
+
# Generated by jeweler
|
2
|
+
# DO NOT EDIT THIS FILE DIRECTLY
|
3
|
+
# Instead, edit Jeweler::Tasks in Rakefile, and run the gemspec command
|
4
|
+
# -*- encoding: utf-8 -*-
|
5
|
+
|
6
|
+
Gem::Specification.new do |s|
|
7
|
+
s.name = %q{som}
|
8
|
+
s.version = "0.0.1"
|
9
|
+
|
10
|
+
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
|
+
s.authors = ["reddavis"]
|
12
|
+
s.date = %q{2009-11-26}
|
13
|
+
s.description = %q{A Self Organising Map}
|
14
|
+
s.email = %q{reddavis@gmail.com}
|
15
|
+
s.extra_rdoc_files = [
|
16
|
+
"LICENSE",
|
17
|
+
"README.rdoc"
|
18
|
+
]
|
19
|
+
s.files = [
|
20
|
+
".document",
|
21
|
+
".gitignore",
|
22
|
+
"LICENSE",
|
23
|
+
"README.rdoc",
|
24
|
+
"Rakefile",
|
25
|
+
"VERSION",
|
26
|
+
"examples/example.rb",
|
27
|
+
"lib/som.rb",
|
28
|
+
"lib/som/node.rb",
|
29
|
+
"som.gemspec",
|
30
|
+
"spec/node_spec.rb",
|
31
|
+
"spec/som_spec.rb",
|
32
|
+
"spec/spec.opts",
|
33
|
+
"spec/spec_helper.rb"
|
34
|
+
]
|
35
|
+
s.homepage = %q{http://github.com/reddavis/som}
|
36
|
+
s.rdoc_options = ["--charset=UTF-8"]
|
37
|
+
s.require_paths = ["lib"]
|
38
|
+
s.rubygems_version = %q{1.3.5}
|
39
|
+
s.summary = %q{A Self Organising Map}
|
40
|
+
s.test_files = [
|
41
|
+
"spec/node_spec.rb",
|
42
|
+
"spec/som_spec.rb",
|
43
|
+
"spec/spec_helper.rb",
|
44
|
+
"examples/example.rb"
|
45
|
+
]
|
46
|
+
|
47
|
+
if s.respond_to? :specification_version then
|
48
|
+
current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
|
49
|
+
s.specification_version = 3
|
50
|
+
|
51
|
+
if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
|
52
|
+
s.add_development_dependency(%q<rspec>, [">= 1.2.9"])
|
53
|
+
else
|
54
|
+
s.add_dependency(%q<rspec>, [">= 1.2.9"])
|
55
|
+
end
|
56
|
+
else
|
57
|
+
s.add_dependency(%q<rspec>, [">= 1.2.9"])
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
data/spec/node_spec.rb
ADDED
@@ -0,0 +1,75 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
|
2
|
+
|
3
|
+
describe "Node" do
|
4
|
+
describe "Initialization" do
|
5
|
+
before do
|
6
|
+
@a = Node.new(5)
|
7
|
+
end
|
8
|
+
|
9
|
+
it "should have 5 weights" do
|
10
|
+
@a.weights.size.should == 5
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
describe "Distance Calculation" do
|
15
|
+
before do
|
16
|
+
@a = Node.new(2)
|
17
|
+
@b = Node.new(2)
|
18
|
+
end
|
19
|
+
|
20
|
+
it "should return 0" do
|
21
|
+
@a.weights[0] = @b.weights[0]
|
22
|
+
@a.weights[1] = @b.weights[1]
|
23
|
+
@a.distance_from(@b.weights).should == 0
|
24
|
+
end
|
25
|
+
|
26
|
+
it "should return 1" do
|
27
|
+
@b.weights[0] = 0.0
|
28
|
+
@b.weights[1] = 0.0
|
29
|
+
|
30
|
+
@a.weights[0] = 1.0
|
31
|
+
@a.weights[1] = 0.0
|
32
|
+
|
33
|
+
@a.distance_from(@b.weights).should == 1
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
describe "Update Weight" do
|
38
|
+
describe "Closest" do
|
39
|
+
before do
|
40
|
+
@a = Node.new(2)
|
41
|
+
@data = [1,2]
|
42
|
+
end
|
43
|
+
|
44
|
+
it "should change the weight" do
|
45
|
+
before = @a.weights.clone
|
46
|
+
@a.update_weight(0.5, @data)
|
47
|
+
@a.weights.should_not == before
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
describe "Neighbor" do
|
52
|
+
before do
|
53
|
+
@a = Node.new(2)
|
54
|
+
@data = [1,2]
|
55
|
+
end
|
56
|
+
|
57
|
+
it "should change the weight" do
|
58
|
+
before = @a.weights.clone
|
59
|
+
@a.update_weight(0.5, @data, 0.23)
|
60
|
+
@a.weights.should_not == before
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
describe "Bucket" do
|
66
|
+
before do
|
67
|
+
@a = Node.new(2)
|
68
|
+
end
|
69
|
+
|
70
|
+
it "should put data into the nodes bucket" do
|
71
|
+
@a << 'some_data'
|
72
|
+
@a.bucket.size.should == 1
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
data/spec/som_spec.rb
ADDED
@@ -0,0 +1,82 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
|
2
|
+
|
3
|
+
describe "Som" do
|
4
|
+
describe "Initialization" do
|
5
|
+
before do
|
6
|
+
@a = SOM.new(:nodes => 10, :dimensions => 5)
|
7
|
+
end
|
8
|
+
|
9
|
+
it "should have 10 nodes" do
|
10
|
+
@a.nodes.size.should == 10
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
describe "Training" do
|
15
|
+
before do
|
16
|
+
@a = SOM.new(:nodes => 1, :dimensions => 2)
|
17
|
+
@data = [[2,3]]
|
18
|
+
end
|
19
|
+
|
20
|
+
it "should change the weight of the best matching node" do
|
21
|
+
before = @a.nodes.map {|x| x.weights.clone}
|
22
|
+
@a.train(@data)
|
23
|
+
after = @a.nodes.map {|x| x.weights}
|
24
|
+
|
25
|
+
before.should_not == after
|
26
|
+
end
|
27
|
+
|
28
|
+
it "should will the nodes bucket with the data" do
|
29
|
+
@a.train(@data)
|
30
|
+
@a.nodes[0].bucket.should_not be_empty
|
31
|
+
end
|
32
|
+
|
33
|
+
it "should preserve data indexes" do
|
34
|
+
data = [[0,0], [0,0.5], [2,4], [6,5]]
|
35
|
+
@a.train(data)
|
36
|
+
|
37
|
+
index_returned = @a.nodes[0].bucket[0][0]
|
38
|
+
data_returned = @a.nodes[0].bucket[0][1]
|
39
|
+
|
40
|
+
data[index_returned].should == data_returned
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
describe "Inspect" do
|
45
|
+
before do
|
46
|
+
@a = SOM.new(:nodes => 1, :dimensions => 2)
|
47
|
+
@data = [[2,3]]
|
48
|
+
end
|
49
|
+
|
50
|
+
it "should show the clusters of data indexes" do
|
51
|
+
@a.train(@data)
|
52
|
+
@a.inspect.should be_an(Array)
|
53
|
+
@a.inspect.size.should == 1
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
describe "Clustering" do
|
58
|
+
before do
|
59
|
+
@a = SOM.new(:nodes => 2, :dimensions => 2)
|
60
|
+
end
|
61
|
+
|
62
|
+
it "should belong to 2 seperate nodes" do
|
63
|
+
data = [[0,0], [999,999]]
|
64
|
+
@a.train(data)
|
65
|
+
@a.inspect[0].should_not be_empty
|
66
|
+
@a.inspect[1].should_not be_empty
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
describe "Classify" do
|
71
|
+
before do
|
72
|
+
@a = SOM.new(:nodes => 2, :dimensions => 2)
|
73
|
+
end
|
74
|
+
|
75
|
+
it "should belong to 2 seperate nodes" do
|
76
|
+
data = [[0,0], [999,999]]
|
77
|
+
@a.train(data)
|
78
|
+
@a.classify([1,1]).should be_an(Array)
|
79
|
+
@a.classify([1,1]).size.should == 1
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
data/spec/spec.opts
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
--color
|
data/spec/spec_helper.rb
ADDED
metadata
ADDED
@@ -0,0 +1,81 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: som
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- reddavis
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
|
12
|
+
date: 2009-11-26 00:00:00 +00:00
|
13
|
+
default_executable:
|
14
|
+
dependencies:
|
15
|
+
- !ruby/object:Gem::Dependency
|
16
|
+
name: rspec
|
17
|
+
type: :development
|
18
|
+
version_requirement:
|
19
|
+
version_requirements: !ruby/object:Gem::Requirement
|
20
|
+
requirements:
|
21
|
+
- - ">="
|
22
|
+
- !ruby/object:Gem::Version
|
23
|
+
version: 1.2.9
|
24
|
+
version:
|
25
|
+
description: A Self Organising Map
|
26
|
+
email: reddavis@gmail.com
|
27
|
+
executables: []
|
28
|
+
|
29
|
+
extensions: []
|
30
|
+
|
31
|
+
extra_rdoc_files:
|
32
|
+
- LICENSE
|
33
|
+
- README.rdoc
|
34
|
+
files:
|
35
|
+
- .document
|
36
|
+
- .gitignore
|
37
|
+
- LICENSE
|
38
|
+
- README.rdoc
|
39
|
+
- Rakefile
|
40
|
+
- VERSION
|
41
|
+
- examples/example.rb
|
42
|
+
- lib/som.rb
|
43
|
+
- lib/som/node.rb
|
44
|
+
- som.gemspec
|
45
|
+
- spec/node_spec.rb
|
46
|
+
- spec/som_spec.rb
|
47
|
+
- spec/spec.opts
|
48
|
+
- spec/spec_helper.rb
|
49
|
+
has_rdoc: true
|
50
|
+
homepage: http://github.com/reddavis/som
|
51
|
+
licenses: []
|
52
|
+
|
53
|
+
post_install_message:
|
54
|
+
rdoc_options:
|
55
|
+
- --charset=UTF-8
|
56
|
+
require_paths:
|
57
|
+
- lib
|
58
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
59
|
+
requirements:
|
60
|
+
- - ">="
|
61
|
+
- !ruby/object:Gem::Version
|
62
|
+
version: "0"
|
63
|
+
version:
|
64
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ">="
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: "0"
|
69
|
+
version:
|
70
|
+
requirements: []
|
71
|
+
|
72
|
+
rubyforge_project:
|
73
|
+
rubygems_version: 1.3.5
|
74
|
+
signing_key:
|
75
|
+
specification_version: 3
|
76
|
+
summary: A Self Organising Map
|
77
|
+
test_files:
|
78
|
+
- spec/node_spec.rb
|
79
|
+
- spec/som_spec.rb
|
80
|
+
- spec/spec_helper.rb
|
81
|
+
- examples/example.rb
|