named_vector 1.0.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 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