sooth 1.0.3 → 2.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 +4 -4
- data/CHANGELOG.md +3 -8
- data/Gemfile +1 -1
- data/Gemfile.lock +61 -34
- data/README.md +7 -4
- data/VERSION +1 -1
- data/ext/sooth_native/extconf.rb +1 -1
- data/ext/sooth_native/native.c +142 -113
- data/ext/sooth_native/sooth_context.h +1 -3
- data/ext/sooth_native/sooth_predictor.c +82 -62
- data/ext/sooth_native/sooth_predictor.h +8 -6
- data/ext/sooth_native/sooth_statistic.h +1 -3
- data/sooth.gemspec +7 -8
- data/spec/memory_spec.rb +24 -24
- data/spec/predictor_spec.rb +153 -115
- metadata +5 -6
data/spec/predictor_spec.rb
CHANGED
@@ -3,176 +3,214 @@ require 'tempfile'
|
|
3
3
|
require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
|
4
4
|
|
5
5
|
describe Sooth::Predictor do
|
6
|
-
let(:
|
6
|
+
let(:error_event) { 42 }
|
7
|
+
let(:predictor) { Sooth::Predictor.new(error_event) }
|
7
8
|
|
8
9
|
describe "#observe" do
|
9
|
-
it "increments
|
10
|
-
expect(predictor.observe(
|
11
|
-
expect(predictor.observe(
|
12
|
-
expect(predictor.observe(
|
10
|
+
it "increments counts" do
|
11
|
+
expect(predictor.observe(1, 3)).to eq(1)
|
12
|
+
expect(predictor.observe(1, 3)).to eq(2)
|
13
|
+
expect(predictor.observe(1, 3)).to eq(3)
|
13
14
|
end
|
14
15
|
|
15
|
-
it "
|
16
|
-
expect(predictor.observe(
|
17
|
-
expect(predictor.observe(
|
18
|
-
expect(predictor.observe(
|
19
|
-
expect(predictor.observe(
|
20
|
-
expect(predictor.observe(
|
21
|
-
expect(predictor.observe(
|
16
|
+
it "sorts and finds contexts" do
|
17
|
+
expect(predictor.observe(2, 3)).to eq(1)
|
18
|
+
expect(predictor.observe(1, 2)).to eq(1)
|
19
|
+
expect(predictor.observe(3, 1)).to eq(1)
|
20
|
+
expect(predictor.observe(1, 2)).to eq(2)
|
21
|
+
expect(predictor.observe(2, 3)).to eq(2)
|
22
|
+
expect(predictor.observe(3, 1)).to eq(2)
|
22
23
|
end
|
23
24
|
end
|
24
25
|
|
25
26
|
describe "#count" do
|
26
|
-
it "returns
|
27
|
-
expect(predictor.count(
|
27
|
+
it "returns zero for an unobserved context" do
|
28
|
+
expect(predictor.count(1)).to eq(0)
|
28
29
|
end
|
29
30
|
|
30
|
-
it "returns the
|
31
|
-
predictor.observe(
|
32
|
-
predictor.observe(
|
33
|
-
predictor.observe(
|
34
|
-
predictor.observe(
|
35
|
-
predictor.observe(
|
36
|
-
predictor.observe(
|
37
|
-
predictor.observe(
|
38
|
-
expect(predictor.count(
|
31
|
+
it "returns the number of observations" do
|
32
|
+
predictor.observe(1, 2)
|
33
|
+
predictor.observe(1, 1)
|
34
|
+
predictor.observe(1, 4)
|
35
|
+
predictor.observe(1, 3)
|
36
|
+
predictor.observe(1, 0)
|
37
|
+
predictor.observe(1, 1)
|
38
|
+
predictor.observe(1, 4)
|
39
|
+
expect(predictor.count(1)).to eq(7)
|
39
40
|
end
|
40
41
|
end
|
41
42
|
|
43
|
+
describe "#size" do
|
44
|
+
it "returns zero for an unobserved context" do
|
45
|
+
expect(predictor.size(1)).to eq(0)
|
46
|
+
end
|
47
|
+
|
48
|
+
it "returns the number of distinct events" do
|
49
|
+
predictor.observe(1, 2)
|
50
|
+
predictor.observe(1, 1)
|
51
|
+
predictor.observe(1, 4)
|
52
|
+
predictor.observe(1, 3)
|
53
|
+
predictor.observe(1, 0)
|
54
|
+
predictor.observe(1, 1)
|
55
|
+
predictor.observe(1, 4)
|
56
|
+
expect(predictor.size(1)).to eq(5)
|
57
|
+
end
|
58
|
+
|
59
|
+
end
|
60
|
+
|
42
61
|
describe "#select" do
|
43
|
-
it "returns the error
|
44
|
-
expect(predictor.select(
|
45
|
-
end
|
46
|
-
|
47
|
-
it "returns the correct
|
48
|
-
predictor.observe(
|
49
|
-
predictor.observe(
|
50
|
-
predictor.observe(
|
51
|
-
predictor.observe(
|
52
|
-
expect(predictor.select(
|
53
|
-
expect(predictor.select(
|
54
|
-
expect(predictor.select(
|
55
|
-
expect(predictor.select(
|
56
|
-
end
|
57
|
-
|
58
|
-
it "returns the error
|
59
|
-
predictor.observe(
|
60
|
-
predictor.observe(
|
61
|
-
predictor.observe(
|
62
|
-
expect(predictor.select(
|
63
|
-
expect(predictor.select(
|
64
|
-
end
|
65
|
-
|
66
|
-
it "selects the correct
|
67
|
-
predictor.observe(
|
68
|
-
predictor.observe(
|
69
|
-
predictor.observe(
|
70
|
-
predictor.observe(
|
71
|
-
predictor.observe(
|
72
|
-
predictor.observe(
|
73
|
-
predictor.observe(
|
74
|
-
predictor.observe(
|
75
|
-
predictor.observe(
|
76
|
-
expect(predictor.select(
|
77
|
-
expect(predictor.select(
|
78
|
-
expect(predictor.select(
|
79
|
-
expect(predictor.select(
|
80
|
-
expect(predictor.select(
|
81
|
-
expect(predictor.select(
|
82
|
-
expect(predictor.select(
|
83
|
-
expect(predictor.select(
|
84
|
-
expect(predictor.select(
|
85
|
-
expect(predictor.select(
|
86
|
-
expect(predictor.select(
|
87
|
-
expect(predictor.select(
|
88
|
-
expect(predictor.select(
|
89
|
-
expect(predictor.select(
|
90
|
-
expect(predictor.select(
|
62
|
+
it "returns the error event for an unobserved context" do
|
63
|
+
expect(predictor.select(1, 1)).to eq(error_event)
|
64
|
+
end
|
65
|
+
|
66
|
+
it "returns the correct event for an observed context" do
|
67
|
+
predictor.observe(1, 4)
|
68
|
+
predictor.observe(1, 3)
|
69
|
+
predictor.observe(1, 4)
|
70
|
+
predictor.observe(1, 5)
|
71
|
+
expect(predictor.select(1, 1)).to eq(3)
|
72
|
+
expect(predictor.select(1, 2)).to eq(4)
|
73
|
+
expect(predictor.select(1, 3)).to eq(4)
|
74
|
+
expect(predictor.select(1, 4)).to eq(5)
|
75
|
+
end
|
76
|
+
|
77
|
+
it "returns the error event for a limit that is out of range" do
|
78
|
+
predictor.observe(1, 4)
|
79
|
+
predictor.observe(1, 3)
|
80
|
+
predictor.observe(1, 5)
|
81
|
+
expect(predictor.select(1, 0)).to eq(error_event)
|
82
|
+
expect(predictor.select(1, 4)).to eq(error_event)
|
83
|
+
end
|
84
|
+
|
85
|
+
it "selects the correct event with many contexts" do
|
86
|
+
predictor.observe(2, 4)
|
87
|
+
predictor.observe(1, 5)
|
88
|
+
predictor.observe(3, 6)
|
89
|
+
predictor.observe(1, 7)
|
90
|
+
predictor.observe(2, 8)
|
91
|
+
predictor.observe(3, 9)
|
92
|
+
predictor.observe(1, 1)
|
93
|
+
predictor.observe(2, 2)
|
94
|
+
predictor.observe(3, 3)
|
95
|
+
expect(predictor.select(2, 0)).to eq(error_event)
|
96
|
+
expect(predictor.select(2, 1)).to eq(2)
|
97
|
+
expect(predictor.select(2, 2)).to eq(4)
|
98
|
+
expect(predictor.select(2, 3)).to eq(8)
|
99
|
+
expect(predictor.select(2, 4)).to eq(error_event)
|
100
|
+
expect(predictor.select(1, 0)).to eq(error_event)
|
101
|
+
expect(predictor.select(1, 1)).to eq(1)
|
102
|
+
expect(predictor.select(1, 2)).to eq(5)
|
103
|
+
expect(predictor.select(1, 3)).to eq(7)
|
104
|
+
expect(predictor.select(1, 4)).to eq(error_event)
|
105
|
+
expect(predictor.select(3, 0)).to eq(error_event)
|
106
|
+
expect(predictor.select(3, 1)).to eq(3)
|
107
|
+
expect(predictor.select(3, 2)).to eq(6)
|
108
|
+
expect(predictor.select(3, 3)).to eq(9)
|
109
|
+
expect(predictor.select(3, 4)).to eq(error_event)
|
91
110
|
end
|
92
111
|
end
|
93
112
|
|
94
113
|
describe "#clear" do
|
95
|
-
|
96
|
-
|
97
|
-
expect(predictor.observe(
|
98
|
-
expect(predictor.observe(
|
99
|
-
expect(predictor.observe([1,2], 3)).to eq(3)
|
114
|
+
it "resets to a blank slate" do
|
115
|
+
expect(predictor.observe(1, 3)).to eq(1)
|
116
|
+
expect(predictor.observe(1, 3)).to eq(2)
|
117
|
+
expect(predictor.observe(1, 3)).to eq(3)
|
100
118
|
predictor.clear
|
101
|
-
expect(predictor.observe(
|
102
|
-
expect(predictor.observe(
|
103
|
-
expect(predictor.observe(
|
119
|
+
expect(predictor.observe(1, 3)).to eq(1)
|
120
|
+
expect(predictor.observe(1, 3)).to eq(2)
|
121
|
+
expect(predictor.observe(1, 3)).to eq(3)
|
104
122
|
end
|
105
|
-
|
106
123
|
end
|
107
124
|
|
108
125
|
describe "#save" do
|
109
|
-
|
110
126
|
it "can save a file and load it back again" do
|
111
127
|
begin
|
112
128
|
file = Tempfile.new('sooth_spec')
|
113
|
-
expect(predictor.observe(
|
114
|
-
expect(predictor.observe(
|
115
|
-
expect(predictor.observe(
|
116
|
-
expect(predictor.observe(
|
129
|
+
expect(predictor.observe(1, 3)).to eq(1)
|
130
|
+
expect(predictor.observe(2, 3)).to eq(1)
|
131
|
+
expect(predictor.observe(1, 3)).to eq(2)
|
132
|
+
expect(predictor.observe(1, 3)).to eq(3)
|
117
133
|
expect { predictor.save(file.path) } .to_not raise_error
|
118
|
-
expect(predictor.count(
|
119
|
-
expect(predictor.count(
|
134
|
+
expect(predictor.count(1)).to eq(3)
|
135
|
+
expect(predictor.count(2)).to eq(1)
|
120
136
|
predictor.clear
|
121
|
-
expect(predictor.count(
|
122
|
-
expect(predictor.count(
|
137
|
+
expect(predictor.count(1)).to eq(0)
|
138
|
+
expect(predictor.count(2)).to eq(0)
|
123
139
|
expect { predictor.load(file.path) }.to_not raise_error
|
124
|
-
expect(predictor.count(
|
125
|
-
expect(predictor.count(
|
126
|
-
expect(predictor.observe(
|
127
|
-
expect(predictor.observe(
|
128
|
-
expect(predictor.observe(
|
129
|
-
expect(predictor.observe(
|
140
|
+
expect(predictor.count(1)).to eq(3)
|
141
|
+
expect(predictor.count(2)).to eq(1)
|
142
|
+
expect(predictor.observe(1, 3)).to eq(4)
|
143
|
+
expect(predictor.observe(1, 1)).to eq(1)
|
144
|
+
expect(predictor.observe(2, 3)).to eq(2)
|
145
|
+
expect(predictor.observe(2, 1)).to eq(1)
|
130
146
|
ensure
|
131
147
|
file.close
|
132
148
|
file.unlink
|
133
149
|
end
|
134
150
|
end
|
151
|
+
end
|
135
152
|
|
153
|
+
describe "#distribution" do
|
154
|
+
it "can iterate over a distribution" do
|
155
|
+
end
|
136
156
|
end
|
137
157
|
|
138
|
-
describe "#
|
158
|
+
describe "#distribution" do
|
159
|
+
it "has no distribution for a new context" do
|
160
|
+
expect(predictor.distribution(1)).to be_nil
|
161
|
+
expect(predictor.count(1)).to eq(0)
|
162
|
+
expect(predictor.uncertainty(1)).to be_nil
|
163
|
+
end
|
164
|
+
|
165
|
+
it "has a correct probability distribution" do
|
166
|
+
predictor.observe(1, 4)
|
167
|
+
predictor.observe(1, 2)
|
168
|
+
predictor.observe(1, 4)
|
169
|
+
predictor.observe(1, 3)
|
170
|
+
dist = Hash[predictor.distribution(1)]
|
171
|
+
expect(dist[1]).to be_nil
|
172
|
+
expect(dist[2]).to eq(0.25)
|
173
|
+
expect(dist[3]).to eq(0.25)
|
174
|
+
expect(dist[4]).to eq(0.5)
|
175
|
+
end
|
176
|
+
end
|
139
177
|
|
178
|
+
describe "#uncertainty" do
|
140
179
|
it "has no uncertainty for a new context" do
|
141
|
-
expect(predictor.uncertainty(
|
142
|
-
expect(predictor.count(
|
143
|
-
expect(predictor.uncertainty(
|
180
|
+
expect(predictor.uncertainty(1)).to be_nil
|
181
|
+
expect(predictor.count(1)).to eq(0)
|
182
|
+
expect(predictor.uncertainty(1)).to be_nil
|
144
183
|
end
|
145
184
|
|
146
185
|
it "has zero uncertainty for a lone context" do
|
147
|
-
predictor.observe(
|
148
|
-
expect(predictor.uncertainty(
|
186
|
+
predictor.observe(1, 3)
|
187
|
+
expect(predictor.uncertainty(1)).to eq(0)
|
149
188
|
end
|
150
189
|
|
151
190
|
it "has maximal uncertainty for a uniform distribution" do
|
152
|
-
(1..256).each { |i| predictor.observe(
|
153
|
-
expect(predictor.uncertainty(
|
191
|
+
(1..256).each { |i| predictor.observe(1, i) }
|
192
|
+
expect(predictor.uncertainty(1)).to eq(8)
|
154
193
|
end
|
155
|
-
|
156
194
|
end
|
157
195
|
|
158
196
|
describe "#surprise" do
|
197
|
+
it "has no surprise for a new context" do
|
198
|
+
expect(predictor.surprise(1, 3)).to be_nil
|
199
|
+
end
|
159
200
|
|
160
|
-
it "has no surprise for a new
|
161
|
-
|
162
|
-
expect(predictor.
|
163
|
-
expect(predictor.surprise([1, 2], 3)).to be_nil
|
201
|
+
it "has no surprise for a new event" do
|
202
|
+
predictor.observe(1, 3)
|
203
|
+
expect(predictor.surprise(1, 4)).to be_nil
|
164
204
|
end
|
165
205
|
|
166
|
-
it "has zero surprise for a lone
|
167
|
-
predictor.observe(
|
168
|
-
expect(predictor.surprise(
|
206
|
+
it "has zero surprise for a lone event" do
|
207
|
+
predictor.observe(1, 3)
|
208
|
+
expect(predictor.surprise(1, 3)).to eq(0)
|
169
209
|
end
|
170
210
|
|
171
211
|
it "has uniform surprise for a uniform distribution" do
|
172
|
-
(1..256).each { |i| predictor.observe(
|
173
|
-
expect(predictor.surprise(
|
212
|
+
(1..256).each { |i| predictor.observe(1, i) }
|
213
|
+
expect(predictor.surprise(1, 3)).to eq(8)
|
174
214
|
end
|
175
|
-
|
176
215
|
end
|
177
|
-
|
178
216
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: sooth
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
4
|
+
version: 2.0.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Jason Hutchens
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2016-04-06 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rspec
|
@@ -100,14 +100,14 @@ dependencies:
|
|
100
100
|
requirements:
|
101
101
|
- - "~>"
|
102
102
|
- !ruby/object:Gem::Version
|
103
|
-
version: '
|
103
|
+
version: '8.2'
|
104
104
|
type: :development
|
105
105
|
prerelease: false
|
106
106
|
version_requirements: !ruby/object:Gem::Requirement
|
107
107
|
requirements:
|
108
108
|
- - "~>"
|
109
109
|
- !ruby/object:Gem::Version
|
110
|
-
version: '
|
110
|
+
version: '8.2'
|
111
111
|
- !ruby/object:Gem::Dependency
|
112
112
|
name: rake-compiler
|
113
113
|
requirement: !ruby/object:Gem::Requirement
|
@@ -142,7 +142,6 @@ executables: []
|
|
142
142
|
extensions:
|
143
143
|
- ext/sooth_native/extconf.rb
|
144
144
|
extra_rdoc_files:
|
145
|
-
- CHANGELOG.md
|
146
145
|
- README.md
|
147
146
|
files:
|
148
147
|
- CHANGELOG.md
|
@@ -183,7 +182,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
183
182
|
version: '0'
|
184
183
|
requirements: []
|
185
184
|
rubyforge_project:
|
186
|
-
rubygems_version: 2.
|
185
|
+
rubygems_version: 2.5.1
|
187
186
|
signing_key:
|
188
187
|
specification_version: 4
|
189
188
|
summary: Sooth is a simple stochastic predictive model.
|