named_vector 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,15 @@
1
+ ---
2
+ !binary "U0hBMQ==":
3
+ metadata.gz: !binary |-
4
+ NGQwYzQ2NzYwOTQyNmM2NzUzODFlMWNhNjllODY2NDliYTAxNjZlZA==
5
+ data.tar.gz: !binary |-
6
+ MDk0MzM2ODNkNTAyOWY2YjNjZTg4MzZhNmYwODRjNTVmYjFmMTEyNQ==
7
+ SHA512:
8
+ metadata.gz: !binary |-
9
+ ZTIxNTE0ODMwYWJiZGIxZjViNzNhMTFkNjI4MTRiM2YzYTNjOTJkM2VlMzI2
10
+ ZDYyMjFhOWIzNGEwZTE3NjQzNzM3NDAyODJlYzY2YWJkYTUzOTk5MzJhMzcy
11
+ Y2Q0NjQxM2JkN2RkOWE3OTIyYTNhZTczM2M0MWM3MmI1ZTIyYmQ=
12
+ data.tar.gz: !binary |-
13
+ YjFiNmVhNmI0NTRkNjhmYTdiY2YzMTVjMzJkY2I1OWMxZmJjYTQ3NzkyMTZm
14
+ OGFlNGFmMmY1YmFhNmM2ZDhmYjg2NDQyOWFlMjc0MTZiMzJlNDE0YmMwZjEx
15
+ YWYwNmQyYjlmNzY2ZDkzOTMyMWQ2OWRlNTFkMzA0MjNkMzVmNzk=
@@ -0,0 +1,162 @@
1
+ # coding: utf-8
2
+ # @author Jonathan Raiman
3
+
4
+
5
+ # Vectors with named dimensions. Infinite dimensional objects with simple vector calculus operations defined.
6
+ class NamedVector
7
+ attr_reader :keys
8
+ alias :dimensions :keys
9
+
10
+ # creates a new named vector
11
+ # @param dims [Hash<String,Fixnum>, Hash<Symbol,Fixnum>, Array<String>, Array<Symbol>] the dimensions to initialize the vectors with, either as a hash of assignments or simply the names of the dimensions.
12
+ def initialize(*dims)
13
+ @vector = Hash.new
14
+ @keys = Set.new
15
+ if dims && dims.first
16
+ case dims.first
17
+ when Hash
18
+ hash = dims.first
19
+ hash.keys.each {|d| modify_dimension(d, hash[d])}
20
+ when String, Symbol, Fixnum
21
+ @keys = Set.new(dims.map {|i| i.to_s})
22
+ dims.each {|d| new_dimension(d)}
23
+ end
24
+ else
25
+ @keys = []
26
+ end
27
+ end
28
+
29
+ # Perform a dot product with another vector
30
+ # @param value [Hash, Fixnum, Float, NamedVector] the vector or scalar to multiply this vector with. Scalar multiplication is destructive.
31
+ def *(value)
32
+ case value
33
+ when Fixnum, Float #scalar multiplication product
34
+ each_dimension {|i| @vector[i] = self[i]*value}
35
+ when NamedVector #dot product
36
+ NamedVector.dot_product(self, value)
37
+ when Hash #dot product
38
+ NamedVector.add_dimension_iterator(value)
39
+ NamedVector.dot_product(self,value)
40
+ else
41
+ raise TypeError, "#{value.class} cannot be coerced to NamedVector."
42
+ end
43
+ end
44
+
45
+ # adds a dimension iterator (iterate through the keys) to a Hash or NamedVector
46
+ # @param obj [Hash, NamedVector] the object to add the method to.
47
+ def self.add_dimension_iterator(obj)
48
+ obj.define_singleton_method(:each_dimension) do |&block|
49
+ self.keys.each do |key|
50
+ block.call(key)
51
+ end
52
+ end
53
+ end
54
+
55
+ # Perform a dot product with another vector
56
+ # @param value [Hash, NamedVector] the vector to multiply this vector with.
57
+ def dot_product(value)
58
+ NamedVector.dot_product(self, value)
59
+ end
60
+
61
+ # Perform a dot product with another vector
62
+ # @param a [Hash, NamedVector] the vector or scalar to multiply this vector with.
63
+ # @param b [Hash, NamedVector] the vector or scalar to multiply this vector with.
64
+ def self.dot_product(a, b)
65
+ sum = 0.0
66
+ if a.keys.length < b.keys.length
67
+ a.each_dimension {|i| sum+=a[i]*b[i]}
68
+ else
69
+ b.each_dimension {|i| sum+=b[i]*a[i]}
70
+ end
71
+ sum
72
+ end
73
+
74
+ # iterates through each dimension of a vector
75
+ def each_dimension
76
+ @keys.each do |key|
77
+ yield(key)
78
+ end
79
+ end
80
+
81
+ # fetches the value at the dimension specified
82
+ # @param key [Symbol, String] the dimension requested
83
+ def [](key)
84
+ @vector.fetch(key.to_s,0.0)
85
+ end
86
+
87
+ def modify_dimension(key,value)
88
+ new_dimension(key.to_s)
89
+ @vector[key.to_s] = value
90
+ end
91
+
92
+ # assigns a value at the dimension specified
93
+ # @param key [Symbol, String] the dimension requested
94
+ # @param value [Fixnum, Float] the assignment for this dimension
95
+ def []=(key,value)
96
+ modify_dimension(key,value)
97
+ end
98
+
99
+ def method_missing(mid, *args) # :nodoc:
100
+ mname = mid.id2name
101
+ len = args.length
102
+ if mname.chomp!('=')
103
+ if len != 1
104
+ raise ArgumentError, "wrong number of arguments (#{len} for 1)", caller(1)
105
+ end
106
+ modify_dimension(mname, args[0])
107
+ elsif len == 0
108
+ @vector.fetch(mname,0.0)
109
+ else
110
+ raise NoMethodError, "undefined method `#{mid}' for #{self}", caller(1)
111
+ end
112
+ end
113
+
114
+ def new_dimension(name)
115
+ sym_name = name.to_sym
116
+ unless respond_to?(sym_name)
117
+ @keys << name
118
+ define_singleton_method(sym_name) { @vector.fetch(name,0.0) }
119
+ define_singleton_method("#{sym_name}=") { |x| @vector[name] = x }
120
+ end
121
+ name
122
+ end
123
+ protected :new_dimension
124
+
125
+ # the squared Euclidean norm of a vector
126
+ def squared_norm
127
+ norm = 0.0
128
+ each_dimension {|i| norm+=self[i]**2}
129
+ norm
130
+ end
131
+
132
+ # the Euclidean norm of a vector
133
+ def norm
134
+ Math.sqrt(squared_norm)
135
+ end
136
+
137
+ # normalizes the vector destructively.
138
+ def normalize
139
+ current_norm = norm
140
+ if current_norm > 0 then self*(1.0/current_norm) end
141
+ end
142
+
143
+ # Mongo conversion
144
+ def to_mongo
145
+ {"keys"=> @keys, "dimensions"=> @keys.map {|i| self[i]}}
146
+ end
147
+
148
+ # Mongo retrieval
149
+ # @param doc [BSON::OrderedHash] the mongo document to retrieve the vector from.
150
+ def self.from_mongo(doc)
151
+ hash = {}
152
+ doc["keys"].each_with_index do |k, index|
153
+ hash[k] = doc["dimensions"][index]
154
+ end
155
+ self.new hash
156
+ end
157
+
158
+ private :new_dimension
159
+ private :method_missing
160
+ private :modify_dimension
161
+
162
+ end
@@ -0,0 +1,56 @@
1
+ require './lib/named_vector.rb'
2
+ describe 'Initialization' do
3
+ it 'should have all the dimensions given in the initilization' do
4
+ dimensions = ["x","y"]
5
+ vector = NamedVector.new(*dimensions)
6
+ vector.dimensions.should include(*dimensions)
7
+ dimensions.each do |dim|
8
+ vector.should respond_to(dim.to_sym).with(0).arguments
9
+ end
10
+ end
11
+
12
+ it 'should initialize from a hash as well' do
13
+ hash_vector = {"x" => 1.0, "y" => 2.0, "z" => 0.0}
14
+ vector = NamedVector.new(hash_vector)
15
+ hash_vector.keys.should eql vector.keys.to_a
16
+ hash_vector.keys.each do |key|
17
+ vector.should respond_to(key.to_sym).with(0).arguments
18
+ vector[key].should eql hash_vector[key]
19
+ end
20
+ end
21
+
22
+ it 'should allow dot products between vectors' do
23
+ dimensions = ["x","y"]
24
+ vectorA = NamedVector.new(:x => 1, :y => 1)
25
+ vectorB = NamedVector.new(:x => 1)
26
+ (vectorA*vectorB).should eql 1.0
27
+ end
28
+
29
+ it 'should allow dot products between mismatched vectors' do
30
+ vectorA = NamedVector.new( "x" => 0, "z" => 2)
31
+ vectorB = NamedVector.new("b" => 10, "z" => 1)
32
+ vectorA.x = 0
33
+ vectorB.z = 1
34
+ vectorA.z = 2
35
+ vectorB.a = 10
36
+ (vectorA*vectorB).should eql 2.0
37
+ end
38
+
39
+ it 'should allow dot products between vectors and hashes' do
40
+ vectorA = NamedVector.new("x","y","z")
41
+ vectorB = {:x => 1, :y => 2, :z => 3}
42
+ vectorA.x = 1
43
+ vectorA.y = 1
44
+ vectorA.z = 1
45
+ (vectorA*vectorB).should eql 6.0
46
+ end
47
+
48
+ it 'should normalize vectors' do
49
+ vector = NamedVector.new("x","y","z")
50
+ vector.x = 1
51
+ vector.y = 1
52
+ vector.z = 1
53
+ vector.normalize
54
+ vector.norm.should be_within(0.01).of 1.0
55
+ end
56
+ end
metadata ADDED
@@ -0,0 +1,47 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: named_vector
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ platform: ruby
6
+ authors:
7
+ - Jonathan Raiman
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2013-11-28 00:00:00.000000000 Z
12
+ dependencies: []
13
+ description: Vectors with named dimensions.
14
+ email: jraiman@mit.edu
15
+ executables: []
16
+ extensions: []
17
+ extra_rdoc_files: []
18
+ files:
19
+ - lib/named_vector.rb
20
+ - specs/initialization.rb
21
+ homepage: http://github.org/JonathanRaiman/named_vector
22
+ licenses:
23
+ - MIT
24
+ metadata: {}
25
+ post_install_message:
26
+ rdoc_options: []
27
+ require_paths:
28
+ - lib
29
+ required_ruby_version: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ! '>='
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ required_rubygems_version: !ruby/object:Gem::Requirement
35
+ requirements:
36
+ - - ! '>='
37
+ - !ruby/object:Gem::Version
38
+ version: '0'
39
+ requirements: []
40
+ rubyforge_project:
41
+ rubygems_version: 2.1.10
42
+ signing_key:
43
+ specification_version: 4
44
+ summary: Vectors with named dimensions.
45
+ test_files:
46
+ - specs/initialization.rb
47
+ has_rdoc: yard