bayesnet 0.0.2 → 0.0.3
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/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
|