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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 9f17178cdfa472aea14e7769f1831f1903ceee2613b6e7b71e1135990dc974ab
4
- data.tar.gz: c55cb95134d4dbb479e89839be7a4634b6bc6a8be52c5c066f1cc1ad21c584a7
3
+ metadata.gz: ca0c7b763a97d4516fb893a7c79716bd4f7279be3fba1b0a9d55f43279aa95cf
4
+ data.tar.gz: 55da869a7d0436bd7eb08a8787bc5d19d56e71e90ac283f4568931c391d3f407
5
5
  SHA512:
6
- metadata.gz: fda426d9dd319b2ad8300a8ba59acae511740cc271729f40b36ce7a7765d8019a901d9d83a2e955ccd964901ee604a3ad96fba092ce41394da5980a77d1bb198
7
- data.tar.gz: 03d4beb66b11ba684007dd49f67ef2ae085ecd5c6e5ca9b8b63327eee109ada3b1f389731543fa6d970bff110230231bc1533022640367a906f57dec1c41bc6d
6
+ metadata.gz: 203475b19fc5bfa4151b4586243ebedce938fcf6a5c5ed3d561f7553ea011e3c8cc797d45360be790f691cecf7462623a0e179160d770454a5bc5cd8ed1a77a2
7
+ data.tar.gz: fd014b35e9f98908694b5a25655f279b8885f01e59e30edf17c34a433758dca7d5c271b6318b5d67df921ce39a63c0e643fb84de241064d8bf8ec95e9a39b94d
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- bayesnet (0.0.2)
4
+ bayesnet (0.0.3)
5
5
 
6
6
  GEM
7
7
  remote: https://rubygems.org/
Binary file
@@ -8,25 +8,27 @@ module Bayesnet
8
8
  end
9
9
 
10
10
  # Specifies variable name together with its values
11
- def var(var_name_to_values)
12
- @vars.merge!(var_name_to_values)
11
+ def scope(var_name_to_values)
12
+ @scope.merge!(var_name_to_values)
13
13
  end
14
14
 
15
- # Specifies function values for args. Latest args is an function value, all previous are argument values
16
- def val(*args)
17
- args = args[0] if args.size == 1 && args[0].is_a?(Array)
18
- @vals[args[0..-2]] = args[-1]
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
- @vars.keys
24
+ @scope.keys
23
25
  end
24
26
 
25
- def [](*args)
26
- key = if args.size == 1 && args[0].is_a?(Hash)
27
- args[0].slice(*var_names).values
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
- args
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 args(*var_names)
40
+ def contextes(*var_names)
39
41
  return [] if var_names.empty?
40
- @vars[var_names[0]].product(*var_names[1..].map { |var_name| @vars[var_name] })
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(@vars.clone, vals)
53
+ self.class.new(@scope.clone, vals)
52
54
  end
53
55
 
54
- def limit_by(evidence)
56
+ def reduce_to(context)
55
57
  # todo: use Hash#except when Ruby 2.6 support no longer needed
56
- evidence_keys_set = evidence.keys.to_set
57
- vars = @vars.reject { |k, _| evidence_keys_set.include?(k) }
58
+ context_keys_set = context.keys.to_set
59
+ scope = @scope.reject { |k, _| context_keys_set.include?(k) }
58
60
 
59
- evidence_vals = evidence.values
60
- indices = evidence.keys.map { |k| index_by_var_name[k] }
61
- vals = @vals.select { |k, v| indices.map { |i| k[i] } == evidence_vals }
62
- vals.transform_keys! { |k| k - evidence_vals }
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(vars, vals)
66
+ self.class.new(scope, vals)
65
67
  end
66
68
 
67
- def reduce(over_vars)
68
- vars = @vars.slice(*over_vars)
69
- indices = vars.keys.map { |k| index_by_var_name[k] }
70
- vals = @vals.group_by { |args, val| indices.map { |i| args[i] } }
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
- reduced = self.class.new(vars, vals)
73
- reduced.normalize
76
+
77
+ self.class.new(scope, vals)
74
78
  end
75
79
 
76
80
  private
77
81
 
78
- def initialize(vars = {}, vals = {})
79
- @vars = vars
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
- @vars.each_with_index { |(k, v), i| @index_by_var_name[k] = i }
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
@@ -20,14 +20,16 @@ module Bayesnet
20
20
  end
21
21
 
22
22
  def distribution(over: [], evidence: {})
23
- limited = joint_distribution.limit_by(evidence)
24
- limited.reduce(over)
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.args(var_name).zip(posterior_distribution.values).max_by(&:last)
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.var node_name => node.values
52
+ factor.scope node_name => node.values
51
53
  end
52
54
 
53
- factor.args(*var_names).each do |args|
54
- val_by_name = var_names.zip(args).to_h
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 args + [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
- var node.name => node.values
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
- var node.name => node.values
29
+ scope node.name => node.values
30
30
  node.parent_nodes.each do |parent_node_name, parent_node|
31
- var parent_node_name => parent_node.values
31
+ scope parent_node_name => parent_node.values
32
32
  end
33
33
  end
34
34
  instance_eval(&block)
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Bayesnet
4
- VERSION = "0.0.2"
4
+ VERSION = "0.0.3"
5
5
  end
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.2
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-28 00:00:00.000000000 Z
11
+ date: 2021-12-30 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: m