bayesnet 0.0.2 → 0.0.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Gemfile.lock +1 -1
- data/doc/morning-mood-model.png +0 -0
- data/lib/bayesnet/factor.rb +35 -31
- data/lib/bayesnet/graph.rb +9 -7
- data/lib/bayesnet/node.rb +3 -3
- data/lib/bayesnet/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: ca0c7b763a97d4516fb893a7c79716bd4f7279be3fba1b0a9d55f43279aa95cf
|
4
|
+
data.tar.gz: 55da869a7d0436bd7eb08a8787bc5d19d56e71e90ac283f4568931c391d3f407
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 203475b19fc5bfa4151b4586243ebedce938fcf6a5c5ed3d561f7553ea011e3c8cc797d45360be790f691cecf7462623a0e179160d770454a5bc5cd8ed1a77a2
|
7
|
+
data.tar.gz: fd014b35e9f98908694b5a25655f279b8885f01e59e30edf17c34a433758dca7d5c271b6318b5d67df921ce39a63c0e643fb84de241064d8bf8ec95e9a39b94d
|
data/Gemfile.lock
CHANGED
data/doc/morning-mood-model.png
CHANGED
Binary file
|
data/lib/bayesnet/factor.rb
CHANGED
@@ -8,25 +8,27 @@ module Bayesnet
|
|
8
8
|
end
|
9
9
|
|
10
10
|
# Specifies variable name together with its values
|
11
|
-
def
|
12
|
-
@
|
11
|
+
def scope(var_name_to_values)
|
12
|
+
@scope.merge!(var_name_to_values)
|
13
13
|
end
|
14
14
|
|
15
|
-
# Specifies
|
16
|
-
def val(*
|
17
|
-
|
18
|
-
|
15
|
+
# Specifies value for a scope context. Value is the last element in `context_and_val`
|
16
|
+
def val(*context_and_val)
|
17
|
+
if context_and_val.size == 1 && context_and_val[0].is_a?(Array)
|
18
|
+
context_and_val = context_and_val[0]
|
19
|
+
end
|
20
|
+
@vals[context_and_val[0..-2]] = context_and_val[-1]
|
19
21
|
end
|
20
22
|
|
21
23
|
def var_names
|
22
|
-
@
|
24
|
+
@scope.keys
|
23
25
|
end
|
24
26
|
|
25
|
-
def [](*
|
26
|
-
key = if
|
27
|
-
|
27
|
+
def [](*context)
|
28
|
+
key = if context.size == 1 && context[0].is_a?(Hash)
|
29
|
+
context[0].slice(*var_names).values
|
28
30
|
else
|
29
|
-
|
31
|
+
context
|
30
32
|
end
|
31
33
|
@vals[key]
|
32
34
|
end
|
@@ -35,9 +37,9 @@ module Bayesnet
|
|
35
37
|
self.class.new(var_distribution.keys, var_distribution.values.map(&:to_a))
|
36
38
|
end
|
37
39
|
|
38
|
-
def
|
40
|
+
def contextes(*var_names)
|
39
41
|
return [] if var_names.empty?
|
40
|
-
@
|
42
|
+
@scope[var_names[0]].product(*var_names[1..].map { |var_name| @scope[var_name] })
|
41
43
|
end
|
42
44
|
|
43
45
|
def values
|
@@ -48,42 +50,44 @@ module Bayesnet
|
|
48
50
|
vals = @vals.clone
|
49
51
|
norm_factor = vals.map(&:last).sum * 1.0
|
50
52
|
vals.each { |k, v| vals[k] /= norm_factor }
|
51
|
-
self.class.new(@
|
53
|
+
self.class.new(@scope.clone, vals)
|
52
54
|
end
|
53
55
|
|
54
|
-
def
|
56
|
+
def reduce_to(context)
|
55
57
|
# todo: use Hash#except when Ruby 2.6 support no longer needed
|
56
|
-
|
57
|
-
|
58
|
+
context_keys_set = context.keys.to_set
|
59
|
+
scope = @scope.reject { |k, _| context_keys_set.include?(k) }
|
58
60
|
|
59
|
-
|
60
|
-
indices =
|
61
|
-
vals = @vals.select { |k, v| indices.map { |i| k[i] } ==
|
62
|
-
vals.transform_keys! { |k| k -
|
61
|
+
context_vals = context.values
|
62
|
+
indices = context.keys.map { |k| index_by_var_name[k] }
|
63
|
+
vals = @vals.select { |k, v| indices.map { |i| k[i] } == context_vals }
|
64
|
+
vals.transform_keys! { |k| k - context_vals }
|
63
65
|
|
64
|
-
self.class.new(
|
66
|
+
self.class.new(scope, vals)
|
65
67
|
end
|
66
68
|
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
69
|
+
# groups by `var_names` having same context and sum out values.
|
70
|
+
def marginalize(var_names)
|
71
|
+
scope = @scope.slice(*var_names)
|
72
|
+
|
73
|
+
indices = scope.keys.map { |k| index_by_var_name[k] }
|
74
|
+
vals = @vals.group_by { |context, val| indices.map { |i| context[i] } }
|
71
75
|
vals.transform_values! { |v| v.map(&:last).sum }
|
72
|
-
|
73
|
-
|
76
|
+
|
77
|
+
self.class.new(scope, vals)
|
74
78
|
end
|
75
79
|
|
76
80
|
private
|
77
81
|
|
78
|
-
def initialize(
|
79
|
-
@
|
82
|
+
def initialize(scope = {}, vals = {})
|
83
|
+
@scope = scope
|
80
84
|
@vals = vals
|
81
85
|
end
|
82
86
|
|
83
87
|
def index_by_var_name
|
84
88
|
return @index_by_var_name if @index_by_var_name
|
85
89
|
@index_by_var_name = {}
|
86
|
-
@
|
90
|
+
@scope.each_with_index { |(k, v), i| @index_by_var_name[k] = i }
|
87
91
|
@index_by_var_name
|
88
92
|
end
|
89
93
|
end
|
data/lib/bayesnet/graph.rb
CHANGED
@@ -20,14 +20,16 @@ module Bayesnet
|
|
20
20
|
end
|
21
21
|
|
22
22
|
def distribution(over: [], evidence: {})
|
23
|
-
|
24
|
-
|
23
|
+
joint_distribution
|
24
|
+
.reduce_to(evidence)
|
25
|
+
.marginalize(over)
|
26
|
+
.normalize
|
25
27
|
end
|
26
28
|
|
27
29
|
# This is MAP query, i.e. Maximum a Posteriory
|
28
30
|
def most_likely_value(var_name, evidence:)
|
29
31
|
posterior_distribution = distribution(over: [var_name], evidence: evidence)
|
30
|
-
mode = posterior_distribution.
|
32
|
+
mode = posterior_distribution.contextes(var_name).zip(posterior_distribution.values).max_by(&:last)
|
31
33
|
mode.first.first
|
32
34
|
end
|
33
35
|
|
@@ -47,15 +49,15 @@ module Bayesnet
|
|
47
49
|
|
48
50
|
factor = Factor.new
|
49
51
|
@nodes.each do |node_name, node|
|
50
|
-
factor.
|
52
|
+
factor.scope node_name => node.values
|
51
53
|
end
|
52
54
|
|
53
|
-
factor.
|
54
|
-
val_by_name = var_names.zip(
|
55
|
+
factor.contextes(*var_names).each do |context|
|
56
|
+
val_by_name = var_names.zip(context).to_h
|
55
57
|
val = nodes.values.reduce(1.0) do |prob, node|
|
56
58
|
prob * node.factor[val_by_name]
|
57
59
|
end
|
58
|
-
factor.val
|
60
|
+
factor.val context + [val]
|
59
61
|
end
|
60
62
|
@joint_distribution = factor.normalize
|
61
63
|
end
|
data/lib/bayesnet/node.rb
CHANGED
@@ -16,7 +16,7 @@ module Bayesnet
|
|
16
16
|
@values = hash_or_array.keys
|
17
17
|
node = self
|
18
18
|
@factor = Factor.build do
|
19
|
-
|
19
|
+
scope node.name => node.values
|
20
20
|
hash_or_array.each do |value, probability|
|
21
21
|
val [value, probability]
|
22
22
|
end
|
@@ -26,9 +26,9 @@ module Bayesnet
|
|
26
26
|
@values = hash_or_array
|
27
27
|
node = self
|
28
28
|
@factor = Factor.build do
|
29
|
-
|
29
|
+
scope node.name => node.values
|
30
30
|
node.parent_nodes.each do |parent_node_name, parent_node|
|
31
|
-
|
31
|
+
scope parent_node_name => parent_node.values
|
32
32
|
end
|
33
33
|
end
|
34
34
|
instance_eval(&block)
|
data/lib/bayesnet/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: bayesnet
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Aleksandr Furmanov
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2021-12-
|
11
|
+
date: 2021-12-30 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: m
|