rb_prob 0.0.1 → 0.0.2

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/examples/alarm.rb CHANGED
@@ -90,7 +90,7 @@ def mk_joint_p(&blk)
90
90
  p_alarm(b, e).dep {|a|
91
91
  p_john(a).dep { |j|
92
92
  p_mary(a).dep {|m|
93
- mkState(if blk then blk.call([b,e,a,j,m])
93
+ mk_const(if blk then blk.call([b,e,a,j,m])
94
94
  else [b,e,a,j,m] end)
95
95
  }
96
96
  }
@@ -122,7 +122,7 @@ def mk_joint_p2( tsts = {}, &blk )
122
122
  condition(!tsts[:john] || tsts[:john] == j) {
123
123
  p_mary(a).dep {|m|
124
124
  condition(!tsts[:mary] || tsts[:mary] == m) {
125
- mkState(if blk then blk.call [b,e,a,j,m] else [b,e,a,j,m] end)
125
+ mk_const(if blk then blk.call [b,e,a,j,m] else [b,e,a,j,m] end)
126
126
  }}
127
127
  }}
128
128
  }}
@@ -133,18 +133,18 @@ end
133
133
  # like mk_joint_p2, but using event_dep directly instead of mixing in
134
134
  # condition-statements
135
135
  def mk_joint_p3 (tsts = {}, &blk)
136
- tst_b = ifJust tsts[:burglary]
137
- tst_e = ifJust tsts[:earthquake]
138
- tst_a = ifJust tsts[:alarm]
139
- tst_j = ifJust tsts[:john]
140
- tst_m = ifJust tsts[:mary]
136
+ tst_b = if_just tsts[:burglary]
137
+ tst_e = if_just tsts[:earthquake]
138
+ tst_a = if_just tsts[:alarm]
139
+ tst_j = if_just tsts[:john]
140
+ tst_m = if_just tsts[:mary]
141
141
 
142
142
  PBurglary.event_dep(tst_b) {|b|
143
143
  PEarthquake.event_dep(tst_e) {|e|
144
144
  p_alarm(b,e).event_dep(tst_a) {|a|
145
145
  p_john(a).event_dep(tst_j) {|j|
146
146
  p_mary(a).event_dep(tst_m) {|m|
147
- mkState(if blk then blk.call [b,e,a,j,m] else [b,e,a,j,m] end)
147
+ mk_const(if blk then blk.call [b,e,a,j,m] else [b,e,a,j,m] end)
148
148
  }
149
149
  }
150
150
  }
@@ -52,36 +52,37 @@ end
52
52
  # P(T|I)
53
53
  # but combine states and save final distribution in constant
54
54
  PTest = PDisease.dep {|i|
55
- pTest(i).dep {|t| mkState([i,t]) }
55
+ pTest(i).dep {|t| mk_const([i,t]) }
56
56
  }
57
57
 
58
58
  testpred = Proc.new {|disease, test| disease == :ILL}
59
59
 
60
- p PTest
60
+ puts "joint probability:"
61
+ puts PTest
61
62
 
62
63
  # using filter we find on PTest which is P(T|I) we find
63
64
  # P( I | T = Positive )
64
- p "probability of I if test is Positive:"
65
- p PTest.filter{|disease, test| test == :Positive}
65
+ puts "probability of I if test is Positive:"
66
+ puts PTest.filter{|disease, test| test == :Positive}
66
67
 
67
68
  # using the testpred function and query we can find the probability of all
68
69
  # events testpred returns true for. In this case P( I = Ill | T = Positive)
69
- p "probability of being ill"
70
- p PTest.filter{|disease,test| test == :Positive}.query? &testpred
70
+ puts "\nprobability of being ill"
71
+ puts PTest.filter{|disease,test| test == :Positive}.query? &testpred
71
72
 
72
73
  # next find the most probable explanation if Test was Positive:
73
- p "most probable"
74
- p PTest.filter{|disease,test| test == :Positive}.most_probable
74
+ puts "\nmost probable"
75
+ puts PTest.filter{|disease,test| test == :Positive}.most_probable
75
76
 
76
77
  # alternatively using condition on the monadic computation directly
77
78
  # and normalizing the result needed multiplications and memory may be reduced:
78
79
  # event_dep is like 'dep {|var| condition(var == :Positive) { ... } }'
79
- p "another way of finding P(I|T=Positive)"
80
- p PDisease.dep {|i|
80
+ puts "\nanother way of finding P(I|T=Positive)"
81
+ puts PDisease.dep {|i|
81
82
  # event_dep will execute block only if
82
83
  # Test was :Positive and return 'nil' else
83
84
  pTest(i).event_dep(just :Positive) {
84
- mkState(i)
85
+ mk_const(i)
85
86
  }
86
87
  }.normalize
87
88
 
data/examples/drugtest.rb CHANGED
@@ -11,14 +11,14 @@ def drugTest(puser = 0.001, p_posifuser = 0.99, p_posifclean = 0.01)
11
11
  choose(puser, :User, :Clean).dep { |user|
12
12
  choose(if user == :User then p_posifuser else p_posifclean end,
13
13
  :Pos, :Neg).dep { |test|
14
- mkState([user, test])
14
+ mk_const([user, test])
15
15
  }
16
16
  }
17
17
  end
18
18
 
19
19
  def drugTest2
20
20
  drugTest.dep {|u,t|
21
- if t == :Pos then mkState(u) else nil end
21
+ if t == :Pos then mk_const(u) else nil end
22
22
  }
23
23
  end
24
24
 
@@ -27,7 +27,7 @@ def drugTest3(puser = 0.001, p_posifuser = 0.99, p_posifclean = 0.01)
27
27
  choose(if user == :User then p_posifuser else p_posifclean end,
28
28
  :Pos, :Neg).dep { |test|
29
29
  condition(test == :Pos) {
30
- mkState user
30
+ mk_const user
31
31
  }
32
32
  }
33
33
  }.normalize
@@ -35,10 +35,18 @@ end
35
35
 
36
36
  #p drugTest2
37
37
 
38
- p drugTest
39
- p drugTest.filter {|u,t| t == :Pos }
40
- p drugTest(0.5).filter {|u,t| t == :Pos}
38
+ puts "test1"
39
+ puts drugTest
41
40
 
42
- p drugTest3
43
- # p drugTest3(0.5)
41
+ puts "\ntest2"
42
+ puts drugTest.filter {|u,t| t == :Pos }
43
+
44
+ puts "\ntest3"
45
+ puts drugTest(0.5).filter {|u,t| t == :Pos}
46
+
47
+ puts "\ntest4"
48
+ puts drugTest3
49
+
50
+ puts "\ntest5"
51
+ puts drugTest3(0.5)
44
52
 
data/examples/spamplan.rb CHANGED
@@ -121,7 +121,7 @@ module SpamDatabaseProbabilities
121
121
  # P(W|S) <- likelyhood
122
122
 
123
123
  def pMsgType # P(S)
124
- enumDist types, @msgCounts
124
+ enum_dist types, @msgCounts
125
125
  end
126
126
 
127
127
  def pWord(word, type) # P(W == word | S == type)
@@ -134,7 +134,7 @@ module SpamDatabaseProbabilities
134
134
  def pHasWord(word, prior = pMsgType)
135
135
  prior.dep {|t|
136
136
  pWord(word, t).event_dep(just true) {
137
- mkState(t)
137
+ mk_const(t)
138
138
  }
139
139
  }.normalize
140
140
  end
@@ -282,14 +282,14 @@ class SpamClassifier
282
282
  f.call uniform(@knowledge.types)
283
283
  end
284
284
 
285
- def score(f = nil, &blk)
286
- pDistance( characteristic(f || blk), uniform(@knowledge.types))
285
+ def score(&blk)
286
+ characteristic(blk).distance uniform(@knowledge.types)
287
287
  end
288
288
 
289
289
  def buildClassifiers
290
290
  @knowledge.knownWords.each {|w,types|
291
291
  s = score {|prior| @knowledge.pHasWord(w,prior)}
292
- probs = adjustMinimums(@knowledge.pHasWord(w, uniform(S)))
292
+ probs = @knowledge.pHasWord(w, uniform(S)).adjust_min
293
293
  yield w, s, probs
294
294
  }
295
295
  end
data/lib/prob.rb CHANGED
@@ -2,19 +2,17 @@
2
2
  # The Probably module provides functions and a discrete Distribution class for
3
3
  # monadic functional probabilistic programming in ruby.
4
4
 
5
- puts 'loading rb_prob'
6
-
7
5
  module Probably
8
6
 
9
7
  # simple helper function running a given block with its first argument and
10
8
  # returns first argument
11
- def block1(x, &blk)
12
- blk.call(x)
13
- x
9
+ def block1(value)
10
+ yield value
11
+ value
14
12
  end
15
13
 
16
14
  # given a block return a new Proc defined on range [0..1]
17
- def mkShapeFunction
15
+ def mk_shape_fn
18
16
  proc { |x|
19
17
  if x < 0 || x > 1.0 then 0 else yield x end
20
18
  }
@@ -22,10 +20,10 @@ module Probably
22
20
 
23
21
  # creates a Proc computing a gaussian distribution
24
22
  # in range [0..1] given a mean and deviation
25
- def normalDistShape(mean, dev)
23
+ def normal_dist(mean, dev)
26
24
  include Math
27
25
 
28
- mkShapeFunction { |x|
26
+ mk_shape_fn { |x|
29
27
  u = (x - mean) / dev
30
28
  exp (-0.5 * u * u) / sqrt(2 * PI)
31
29
  }
@@ -36,23 +34,23 @@ module Probably
36
34
  include Enumerable
37
35
 
38
36
  protected
39
- def initializeLists(data, shape)
37
+ def init_lsts(data, shape)
40
38
  @map = Hash.new(0)
41
39
  count = data.length
42
- data.each_with_index { |val, i|
43
- @map[val] += shape.call( Float(i + 1) / count )
40
+ data.each_with_index { |value, index|
41
+ @map[value] += shape.call( Float(index + 1) / count )
44
42
  }
45
43
  end
46
44
 
47
- def initializeMap(m)
45
+ def init_map(map)
48
46
  @map = Hash.new(0)
49
- m.each { |k,v| @map[k] = v }
50
- self.normalizeProbabilities
47
+ map.each { |value, prob| @map[value] = prob }
48
+ self.normalize_probabilities
51
49
  end
52
50
 
53
- def normalizeProbabilities
51
+ def normalize_probabilities
54
52
  sum = Float( @map.values.inject(:+) )
55
- @map.keys.each { |k| @map[k] /= sum } if sum != 1.0
53
+ @map.keys.each { |value| @map[value] /= sum } if sum != 1.0
56
54
  end
57
55
 
58
56
  public
@@ -72,13 +70,13 @@ module Probably
72
70
  when :MAP
73
71
  @map = data[0]
74
72
  when :MAPCOPY
75
- initializeMap(data[0])
73
+ init_map(data[0])
76
74
  when :LISTS
77
- initializeLists(data[0], data[1])
75
+ init_lsts(data[0], data[1])
78
76
  else
79
77
  raise "unable to create probability distribution"
80
78
  end
81
- self.normalizeProbabilities
79
+ self.normalize_probabilities
82
80
  end
83
81
 
84
82
  # set of keys in distribution
@@ -92,7 +90,7 @@ module Probably
92
90
  # to compute normalization of bayes theorem
93
91
  def normalize
94
92
  if @map[nil] > 0.0
95
- filter { |v| v != nil }
93
+ filter { |value| value != nil }
96
94
  else
97
95
  @self
98
96
  end
@@ -115,30 +113,36 @@ module Probably
115
113
  # randomly pick a key-value with respect to its probability
116
114
  # in given distribution
117
115
  def pick
118
- r = rand
116
+ tst = rand
119
117
  sum = 0
120
- for k,p in @map
121
- sum += p
122
- return k,p if r < sum
118
+ @map.each do |value, prob|
119
+ sum += prob
120
+ return value,prob if tst < sum
123
121
  end
124
122
  return nil
125
123
  end
126
124
 
127
125
  def each
128
- @map.each { |k, p| yield p, k }
126
+ @map.each { |value, prob| yield prob, value }
129
127
  end
130
128
 
131
129
  def map
132
130
  tmp = Hash.new(0)
133
- for k,p in @map
134
- tmp[yield(k)] += p
131
+ @map.each do |value, prob|
132
+ tmp[yield(value)] += prob
135
133
  end
136
134
  Distribution.new(:MAP, tmp)
137
135
  end
138
136
 
139
137
  def filter
140
- Distribution.new :MAP, @map.reject { |k,v|
141
- !(yield k)
138
+ Distribution.new :MAP, @map.reject { |value,prob|
139
+ !(yield value)
140
+ }
141
+ end
142
+
143
+ def reject
144
+ Distribution.new :MAP, @map.reject { |value, prob|
145
+ yield value
142
146
  }
143
147
  end
144
148
 
@@ -152,61 +156,65 @@ module Probably
152
156
  def join
153
157
  tmp = Hash.new(0)
154
158
 
155
- for dist,p1 in @map
156
- for p2, k in dist
157
- tmp[k] += p1 * p2
159
+ @map.each do |dist, p1|
160
+ dist.each do |p2, value|
161
+ tmp[value] += p1 * p2
158
162
  end
159
163
  end
160
164
  Distribution.new(:MAP, tmp)
161
165
  end
162
166
 
163
167
  def dep
164
- m = Hash.new(0)
165
- for k1,p1 in @map
166
- tmp = yield k1
167
- if tmp != nil
168
- for p2, k in tmp
169
- m[k] += p1 * p2
168
+ Distribution.new :MAP, block1(Hash.new(0)) {|m|
169
+ @map.each do |key, p1|
170
+ tmp = yield key
171
+ if tmp != nil
172
+ tmp.each do |p2, value|
173
+ m[value] += p1 * p2
174
+ end
175
+ else
176
+ m[nil] += p1
170
177
  end
171
178
  end
172
- end
173
- Distribution.new(:MAP, m)
179
+ }
174
180
  end
175
181
 
176
182
  def event_dep(pred)
177
- self.dep {|x|
178
- if !pred.call x
179
- mkState nil
183
+ self.dep {|value|
184
+ if !pred.call value
185
+ nil
180
186
  else
181
- yield x
187
+ yield value
182
188
  end
183
189
  }
184
190
  end
185
191
 
186
192
  def mult(dist2)
187
193
  self.dep do |k|
188
- if block_given? then dist2.map { |k2| yield(k, k2) }
189
- else dist2.map { |k2| [k, k2] }
194
+ if block_given?
195
+ dist2.map { |k2| yield(k, k2) }
196
+ else
197
+ dist2.map { |k2| [k, k2] }
190
198
  end
191
199
  end
192
200
  end
193
201
 
194
- def * (dist2)
195
- self.mult dist2
202
+ def * (other)
203
+ self.mult other
196
204
  end
197
205
 
198
206
  # computes expectation given that keys in distribution
199
207
  # are numeric
200
208
  def expectation
201
- @map.reduce(0) {|sum, (k,p)| sum + k.to_f * p }
209
+ @map.reduce(0) {|sum, (value, p)| sum + value.to_f * p }
202
210
  end
203
211
 
204
212
  # computes variance given that keys in distribution
205
213
  # are numeric
206
214
  def variance
207
215
  expected = self.expectation
208
- @map.reduce(0) {|sum, (k,p)|
209
- tmp = (k.to_f - expectation)
216
+ @map.reduce(0) {|sum, (value,p)|
217
+ tmp = (value.to_f - expectation)
210
218
  sum + tmp * tmp * p
211
219
  }
212
220
  end
@@ -218,110 +226,114 @@ module Probably
218
226
  end
219
227
 
220
228
  def to_s
221
- @map.reduce("") { |str,(k,p)|
222
- str + "#{k} : #{p * 100} %\n"
229
+ @map.reduce("") { |str,(value, prob)|
230
+ str + "#{value} : #{prob * 100} %\n"
223
231
  }
224
232
  end
233
+
234
+ def distance(other)
235
+ (self.keys | other.keys).reduce(0) {|sum, value|
236
+ tmp = self.probability(value) - other.probability(value)
237
+ sum + tmp * tmp
238
+ }
239
+ end
240
+
241
+ def adjust_min(new_min = 0.01)
242
+ tmp = Hash.new(0)
243
+ self.each do |prob, value|
244
+ tmp[value] = if prob > new_min then prob else new_min end
245
+ end
246
+ Distribution.new :MAP, tmp
247
+ end
225
248
  end
226
249
 
227
250
  # create uniformly distributed Distribution from array of values
228
251
  def uniform(data)
229
- Distribution.new :LISTS, data, mkShapeFunction {|x| 1}
252
+ Distribution.new :LISTS, data, mk_shape_fn {|x| 1}
230
253
  end
231
254
 
232
255
  # creates linearly distributed Distribution from array of values
233
256
  def linear(data)
234
- Distribution.new :LISTS, data, mkShapeFunction {|x| x }
257
+ Distribution.new :LISTS, data, mk_shape_fn {|x| x }
235
258
  end
236
259
 
237
260
  # creates exp(-x) distributed Distribution from array of values
238
- def negExp(data)
239
- Distribution.new :LISTS, data, mkShapeFunction {|x| Math.exp(-x) }
261
+ def nexp(data)
262
+ Distribution.new :LISTS, data, mk_shape_fn {|x| Math.exp(-x) }
240
263
  end
241
264
 
242
265
  # creates Distribution from array of values using a gaussian distribution
243
266
  def normal(data, mean = 0.5, dev = 0.5)
244
- Distribution.new :LISTS, data, normalDistShape(mean, dev)
267
+ Distribution.new :LISTS, data, normal_dist(mean, dev)
245
268
  end
246
269
 
247
270
  # creates a distribution from first array holding the distribution
248
271
  # values and second one the corresponding probabilities (do be normalized)
249
272
  # - data: array of input values
250
273
  # - dist: array of probabilities
251
- def enumDist(data, dist)
252
- if data.length != dist.length
274
+ def enum_dist(data, dist)
275
+ dist_len = dist.length
276
+
277
+ if data.length != dist_len
253
278
  raise "data and distribution length must be equal"
254
279
  end
255
280
 
256
- Distribution.new :LISTS, data, mkShapeFunction {|i| dist[i * dist.length - 1]}
281
+ Distribution.new :LISTS, data, mk_shape_fn {|i|
282
+ dist[i * dist_len - 1]
283
+ }
257
284
  end
258
285
 
259
286
  # Creates a new probability distribution from given map:
260
287
  # m = { key1 => probability1, key2 => probability2, key3 => ... }
261
- def mapDist(m)
288
+ def dist_from_map(m)
262
289
  Distribution.new :MAPCOPY, m
263
290
  end
264
291
 
265
- def distWithShape(data, &blk)
266
- Distribution.new :LISTS, data, mkShapeFunction(&blk)
292
+ def dist_with(data, &blk)
293
+ Distribution.new :LISTS, data, mk_shape_fn(&blk)
267
294
  end
268
295
 
269
- def choose(p, elem1, elem2)
270
- tmp = Hash.new(0)
271
- tmp[elem1] = p
272
- tmp[elem2] = 1 - p
273
- Distribution.new :MAP, tmp
296
+ def choose(prob, value, other)
297
+ Distribution.new :MAP, block1(Hash.new(0)) { |tmp|
298
+ tmp[value] = prob
299
+ tmp[other] = 1 - prob
300
+ }
274
301
  end
275
302
 
276
- def mkState(a)
277
- tmp = Hash.new(0)
278
- tmp[a] = 1
279
- Distribution.new :MAP, tmp
303
+ def mk_const(value)
304
+ Distribution.new :MAP, block1(Hash.new(0)) { |tmp|
305
+ tmp[value] = 1
306
+ }
280
307
  end
281
308
 
282
- def histogram(a)
283
- block1(Hash.new(0)) do |r|
284
- for x in a
285
- r[x] += 1
309
+ def histogram(iter)
310
+ block1(Hash.new(0)) do |tmp|
311
+ iter.each do |value|
312
+ tmp[value] += 1
286
313
  end
287
314
  end
288
315
  end
289
316
 
290
- def condition(b)
291
- if b then yield else mkState nil end
317
+ def condition(bool)
318
+ if bool then yield else nil end
292
319
  end
293
320
 
294
321
  # events
295
- def mkEvent(&f)
296
- f
322
+ def mk_event(&fn)
323
+ fn
297
324
  end
298
325
 
299
- def just(x)
300
- mkEvent {|y| x == y}
326
+ def just(default)
327
+ mk_event {|value| default == value}
301
328
  end
302
329
 
303
- def ifJust(x)
304
- if x == nil then proc {|y| true }
305
- else proc {|y| x == y } end
330
+ def if_just(default)
331
+ if default == nil then proc {|value| true }
332
+ else proc {|value| default == value } end
306
333
  end
307
334
 
308
- def oneOf(*elems)
309
- proc {|y| elems.include? y }
310
- end
311
-
312
- def pDistance(dist1, dist2)
313
- (dist1.keys | dist2.keys).reduce(0) {|sum,k|
314
- tmp = dist1.probability(k) - dist2.probability(k)
315
- sum + tmp * tmp
316
- }
317
- end
318
-
319
- def adjustMinimums(dist, newMin = 0.01)
320
- tmp = Hash.new(0)
321
- dist.each do |p,k|
322
- tmp[k] = if p > newMin then p else newMin end
323
- end
324
- Distribution.new :MAP, tmp
335
+ def one_of(*elems)
336
+ proc {|value| elems.include? value }
325
337
  end
326
338
 
327
339
  end
metadata CHANGED
@@ -1,7 +1,12 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rb_prob
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.1
4
+ prerelease: false
5
+ segments:
6
+ - 0
7
+ - 0
8
+ - 2
9
+ version: 0.0.2
5
10
  platform: ruby
6
11
  authors:
7
12
  - Steffen Siering
@@ -9,7 +14,7 @@ autorequire:
9
14
  bindir: bin
10
15
  cert_chain: []
11
16
 
12
- date: 2010-03-21 00:00:00 +01:00
17
+ date: 2010-03-24 00:00:00 +01:00
13
18
  default_executable:
14
19
  dependencies: []
15
20
 
@@ -42,18 +47,20 @@ required_ruby_version: !ruby/object:Gem::Requirement
42
47
  requirements:
43
48
  - - ">="
44
49
  - !ruby/object:Gem::Version
50
+ segments:
51
+ - 0
45
52
  version: "0"
46
- version:
47
53
  required_rubygems_version: !ruby/object:Gem::Requirement
48
54
  requirements:
49
55
  - - ">="
50
56
  - !ruby/object:Gem::Version
57
+ segments:
58
+ - 0
51
59
  version: "0"
52
- version:
53
60
  requirements: []
54
61
 
55
62
  rubyforge_project:
56
- rubygems_version: 1.3.5
63
+ rubygems_version: 1.3.6
57
64
  signing_key:
58
65
  specification_version: 3
59
66
  summary: monadic probabilistic programming for ruby