bn4r 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG +3 -0
- data/README +62 -0
- data/Rakefile +85 -0
- data/lib/bn4r/bn.rb +249 -0
- data/lib/bn4r/bn_algorithms.rb +215 -0
- data/lib/bn4r/bn_table_probabilities.rb +65 -0
- data/lib/bn4r/version.rb +9 -0
- data/lib/bn4r.rb +11 -0
- data/test/bn4r_test.rb +290 -0
- data/test/test_helper.rb +2 -0
- metadata +74 -0
data/CHANGELOG
ADDED
data/README
ADDED
@@ -0,0 +1,62 @@
|
|
1
|
+
|
2
|
+
= Bayesian Networks for Ruby ( bn4r )
|
3
|
+
|
4
|
+
bn4r is a bayesian networks library on ruby that provides
|
5
|
+
the user with classes for create bayesian networks and
|
6
|
+
diverse algorithms for solve them.
|
7
|
+
|
8
|
+
Its algorithms implementation are based on: S.Russell, P.Norving, "Artificial
|
9
|
+
Intelligence, A Modern Approach", 2nd Edition.
|
10
|
+
|
11
|
+
Website:
|
12
|
+
http://bn4r.rubyforge.org
|
13
|
+
|
14
|
+
Rubyforge Project:
|
15
|
+
http://rubyforge.org/projects/bn4r
|
16
|
+
|
17
|
+
= Dependencies
|
18
|
+
|
19
|
+
* rgl-0.2.3 ( Ruby Graph Library ), http://rgl.rubyforge.org
|
20
|
+
|
21
|
+
= Design principles
|
22
|
+
|
23
|
+
The library consists on the object BayesNet thinked to be filled with
|
24
|
+
BayesNetNode, these objects are defined in bn.rb. BayesNet object is a
|
25
|
+
especialization of RGL::DirectedAdjacencyGraph ( http://rgl.rubyforge.org ).
|
26
|
+
|
27
|
+
|
28
|
+
The file bn_algorithms.rb has the implementation of the inference algorithms
|
29
|
+
that can be used to solve BayesNet structures.
|
30
|
+
|
31
|
+
Finally, a set of objects and methods are given to automaticly fill BayesNetNode
|
32
|
+
probabilities tables.
|
33
|
+
|
34
|
+
|
35
|
+
= Documentation
|
36
|
+
|
37
|
+
Documentation can be found at
|
38
|
+
http://bn4r.rubyforge.org/rdoc
|
39
|
+
or can be generated using rdoc tool under the source code with:
|
40
|
+
rdoc README lib
|
41
|
+
|
42
|
+
= Credits
|
43
|
+
|
44
|
+
Thanks to N�ria Bel ( http://www.upf.edu/pdi/iula/nuria.bel ) for her work in this project
|
45
|
+
without her it cannot be done.
|
46
|
+
|
47
|
+
Thanks to Ryan Dahl for his work in http://www.math.rochester.edu/people/grads/rld/bayesnets
|
48
|
+
that was the inspiration of the project.
|
49
|
+
|
50
|
+
Also thanks to all the ruby community.
|
51
|
+
|
52
|
+
== Copying
|
53
|
+
|
54
|
+
This work is developed by Sergio Espeja ( http://www.upf.edu/pdi/iula/sergio.espeja, sergio.espeja at gmail.com )
|
55
|
+
mainly in Institut Universitari de Ling�istica Aplicada of Universitat Pompeu Fabra ( http://www.iula.upf.es ),
|
56
|
+
and also in bee.com.es ( http://bee.com.es ).
|
57
|
+
|
58
|
+
It is free software, and may be redistributed under GPL license.
|
59
|
+
|
60
|
+
== Support
|
61
|
+
|
62
|
+
Please contact me in http://rubyforge.org/projects/bn4r.
|
data/Rakefile
ADDED
@@ -0,0 +1,85 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'rake'
|
3
|
+
require 'rake/clean'
|
4
|
+
require 'rake/testtask'
|
5
|
+
require 'rake/packagetask'
|
6
|
+
require 'rake/gempackagetask'
|
7
|
+
require 'rake/rdoctask'
|
8
|
+
require 'rake/contrib/rubyforgepublisher'
|
9
|
+
require 'fileutils'
|
10
|
+
include FileUtils
|
11
|
+
require File.join(File.dirname(__FILE__), 'lib', 'bn4r', 'version')
|
12
|
+
|
13
|
+
AUTHOR = "Sergio Espeja"
|
14
|
+
EMAIL = "sergio.espeja@gmail.com"
|
15
|
+
DESCRIPTION = "bn4r is a bayesian networks library on ruby that provides the user with classes for create bayesian networks and diverse algorithms for solve them."
|
16
|
+
RUBYFORGE_PROJECT = "bn4r"
|
17
|
+
HOMEPATH = "http://#{RUBYFORGE_PROJECT}.rubyforge.org"
|
18
|
+
BIN_FILES = %w( )
|
19
|
+
|
20
|
+
|
21
|
+
NAME = "bn4r"
|
22
|
+
REV = File.read(".svn/entries")[/committed-rev="(d+)"/, 1] rescue nil
|
23
|
+
VERS = ENV['VERSION'] || (Bn4r::VERSION::STRING + (REV ? ".#{REV}" : ""))
|
24
|
+
CLEAN.include ['**/.*.sw?', '*.gem', '.config']
|
25
|
+
RDOC_OPTS = ['--quiet', '--title', "bn4r documentation",
|
26
|
+
"--opname", "index.html",
|
27
|
+
"--line-numbers",
|
28
|
+
"--main", "README",
|
29
|
+
"--inline-source"]
|
30
|
+
|
31
|
+
desc "Packages up bn4r gem."
|
32
|
+
task :default => [:test]
|
33
|
+
task :package => [:clean]
|
34
|
+
|
35
|
+
Rake::TestTask.new("test") { |t|
|
36
|
+
t.libs << "test"
|
37
|
+
t.pattern = "test/**/*_test.rb"
|
38
|
+
t.verbose = true
|
39
|
+
}
|
40
|
+
|
41
|
+
spec =
|
42
|
+
Gem::Specification.new do |s|
|
43
|
+
s.name = NAME
|
44
|
+
s.version = VERS
|
45
|
+
s.platform = Gem::Platform::RUBY
|
46
|
+
s.has_rdoc = true
|
47
|
+
s.extra_rdoc_files = ["README", "CHANGELOG"]
|
48
|
+
s.rdoc_options += RDOC_OPTS + ['--exclude', '^(examples|extras)/']
|
49
|
+
s.summary = DESCRIPTION
|
50
|
+
s.description = DESCRIPTION
|
51
|
+
s.author = AUTHOR
|
52
|
+
s.email = EMAIL
|
53
|
+
s.homepage = HOMEPATH
|
54
|
+
s.executables = BIN_FILES
|
55
|
+
s.rubyforge_project = RUBYFORGE_PROJECT
|
56
|
+
s.bindir = "bin"
|
57
|
+
s.require_path = "lib"
|
58
|
+
s.autorequire = "bn4r"
|
59
|
+
|
60
|
+
s.add_dependency('rgl', '>=0.2.3')
|
61
|
+
#s.required_ruby_version = '>= 1.8.2'
|
62
|
+
|
63
|
+
s.files = %w(README CHANGELOG Rakefile) +
|
64
|
+
Dir.glob("{bin,doc,test,lib,templates,generator,extras,website,script}/**/*") +
|
65
|
+
Dir.glob("ext/**/*.{h,c,rb}") +
|
66
|
+
Dir.glob("examples/**/*.rb") +
|
67
|
+
Dir.glob("tools/*.rb")
|
68
|
+
|
69
|
+
# s.extensions = FileList["ext/**/extconf.rb"].to_a
|
70
|
+
end
|
71
|
+
|
72
|
+
Rake::GemPackageTask.new(spec) do |p|
|
73
|
+
p.need_tar = true
|
74
|
+
p.gem_spec = spec
|
75
|
+
end
|
76
|
+
|
77
|
+
task :install do
|
78
|
+
name = "#{NAME}-#{VERS}.gem"
|
79
|
+
sh %{rake package}
|
80
|
+
sh %{sudo gem install pkg/#{name}}
|
81
|
+
end
|
82
|
+
|
83
|
+
task :uninstall => [:clean] do
|
84
|
+
sh %{sudo gem uninstall #{NAME}}
|
85
|
+
end
|
data/lib/bn4r/bn.rb
ADDED
@@ -0,0 +1,249 @@
|
|
1
|
+
##############################################################
|
2
|
+
#
|
3
|
+
# Bayesian Network Library for Ruby
|
4
|
+
#
|
5
|
+
# Author: Sergio Espeja ( http://www.upf.edu/pdi/iula/sergio.espeja, sergio.espeja at gmail.com )
|
6
|
+
#
|
7
|
+
# Developed in: IULA ( http://www.iula.upf.es ) and
|
8
|
+
# in bee.com.es ( http://bee.com.es )
|
9
|
+
#
|
10
|
+
# Based on work by Ryan Dahl in
|
11
|
+
# http://www.math.rochester.edu/people/grads/rld/bayesnets
|
12
|
+
#
|
13
|
+
##############################################################
|
14
|
+
require 'rgl/adjacency'
|
15
|
+
require 'rgl/dot'
|
16
|
+
|
17
|
+
include RGL
|
18
|
+
|
19
|
+
class BayesNet < DirectedAdjacencyGraph
|
20
|
+
|
21
|
+
# DirectedAdjacencyGraph redefined methods
|
22
|
+
# ----------------------------------------
|
23
|
+
|
24
|
+
# Adds a directed edge between parent and child BayesNetNodes labeled
|
25
|
+
# with tag ( if tag is included, othewise the label is nil ).
|
26
|
+
def add_edge(parent, child, tag=nil)
|
27
|
+
raise "Nodes must be of the class BayesNetNodes" if parent.class != BayesNetNode or child.class != BayesNetNode
|
28
|
+
edge = super(parent, child)
|
29
|
+
child.parents << parent
|
30
|
+
child.relations << tag if !tag.nil?
|
31
|
+
edge
|
32
|
+
end
|
33
|
+
|
34
|
+
# BN Methods
|
35
|
+
# ----------
|
36
|
+
|
37
|
+
# Clears the value of all BayesNetNodes in Bayes Net.
|
38
|
+
def clear_values!
|
39
|
+
vertices.each { |v| v.clear_value }
|
40
|
+
end
|
41
|
+
|
42
|
+
# Gets the variable with given name.
|
43
|
+
def get_variable( text )
|
44
|
+
vertices.each { |v| return v if v.name == text }
|
45
|
+
end
|
46
|
+
|
47
|
+
# Returns the root nodes of the Bayes Net.
|
48
|
+
def roots
|
49
|
+
vertices.select { |v| root?(v) }
|
50
|
+
end
|
51
|
+
|
52
|
+
# Returns true/false if given Node is root.
|
53
|
+
def root?(v)
|
54
|
+
return true if num_parents(v) == 0
|
55
|
+
false
|
56
|
+
end
|
57
|
+
|
58
|
+
# Returns the number of parents of a node.
|
59
|
+
def num_parents(v)
|
60
|
+
return v.parents.size
|
61
|
+
end
|
62
|
+
|
63
|
+
# Return the probability of a distribution in the bayes net
|
64
|
+
# all nodes in the Bayes Net must have a value, otherwise
|
65
|
+
# will raise a exception
|
66
|
+
def inference_by_enumeration
|
67
|
+
prob = 1.0;
|
68
|
+
vertices.each {|v| prob = prob * p_v_cond_parents(v)}
|
69
|
+
prob
|
70
|
+
end
|
71
|
+
|
72
|
+
# Returns the probability of a node conditioned to his parents:
|
73
|
+
# P(v|parents(v))
|
74
|
+
def p_v_cond_parents(v)
|
75
|
+
givens_assignments = v.parents.collect {|parents| parents.value}
|
76
|
+
v.get_probability(v.value, givens_assignments).to_f
|
77
|
+
end
|
78
|
+
|
79
|
+
# Returns true if all nodes have values.
|
80
|
+
def all_nodes_with_values?
|
81
|
+
vertices.select {|v| !v.value.nil? }.size == vertices.size
|
82
|
+
end
|
83
|
+
|
84
|
+
# Returns nodes ordered by dependencies ( from those who haven't ( roots )
|
85
|
+
# to leaves ).
|
86
|
+
def nodes_ordered_by_dependencies(nodes = vertices, bn_ordered = Array.new)
|
87
|
+
nodes.each { |v|
|
88
|
+
next if bn_ordered.include?(v)
|
89
|
+
nodes_ordered_by_dependencies(v.parents, bn_ordered) if !v.root?
|
90
|
+
bn_ordered << v
|
91
|
+
}
|
92
|
+
return bn_ordered.flatten
|
93
|
+
end
|
94
|
+
|
95
|
+
|
96
|
+
end
|
97
|
+
|
98
|
+
class BayesNetNode
|
99
|
+
attr_reader :name, :outcomes, :extra_info, :value, :relations
|
100
|
+
|
101
|
+
# create de BayesNetNode
|
102
|
+
# name is the identifier of the node in the Bayesian Network
|
103
|
+
# outcomes is an array with the posible values that this node
|
104
|
+
# can have, by default [true, false].
|
105
|
+
# [optional] extra_info extra information that can be putted in the node
|
106
|
+
def initialize ( name , outcomes = [true, false], extra_info = nil)
|
107
|
+
@name = name
|
108
|
+
@outcomes = outcomes
|
109
|
+
@extra_info = extra_info
|
110
|
+
@givens = []
|
111
|
+
@relations = []
|
112
|
+
end
|
113
|
+
|
114
|
+
# Returns a copy of the node itself
|
115
|
+
def copy
|
116
|
+
tmp = BayesNetNode.new(@name, @outcomes, @extra_info)
|
117
|
+
tmp.set_value(@value)
|
118
|
+
tmp.set_probability_table(@givens, @table)
|
119
|
+
tmp
|
120
|
+
end
|
121
|
+
|
122
|
+
def set_value(value)
|
123
|
+
@value = value
|
124
|
+
end
|
125
|
+
|
126
|
+
def clear_value
|
127
|
+
@value = nil
|
128
|
+
end
|
129
|
+
|
130
|
+
# Return node parents
|
131
|
+
def parents
|
132
|
+
return @givens
|
133
|
+
end
|
134
|
+
|
135
|
+
# Return node relations
|
136
|
+
def relations
|
137
|
+
return @relations
|
138
|
+
end
|
139
|
+
|
140
|
+
# Return the number of parents
|
141
|
+
def num_parents
|
142
|
+
parents.size
|
143
|
+
end
|
144
|
+
|
145
|
+
# Returns true if the node is a root node ( doesn't have parents ).
|
146
|
+
def root?
|
147
|
+
return true if num_parents == 0
|
148
|
+
false
|
149
|
+
end
|
150
|
+
|
151
|
+
# Returns true if all parents of the node in the bn have values
|
152
|
+
def all_parents_with_values?
|
153
|
+
parents.select {|v| !v.value.nil? }.size == parents.size
|
154
|
+
end
|
155
|
+
|
156
|
+
def to_s
|
157
|
+
return name + (value.nil? ? "" : (" = " + value.to_s))
|
158
|
+
end
|
159
|
+
|
160
|
+
# if givens is nil, then internal givens is assumed
|
161
|
+
# table must have probability values in order like BAD!!!
|
162
|
+
# [g0=pos0 & g1=pos0 & ... & node_value=pos0, ..., g0=pos0 & g1=pos0 & ... & node_value=posN,
|
163
|
+
#
|
164
|
+
def set_probability_table (givens, table)
|
165
|
+
# perhaps we should do some error checking on the table entries here?
|
166
|
+
@table_is_a_proc = (table.class != Array)
|
167
|
+
@givens = givens if !givens.nil?
|
168
|
+
|
169
|
+
raise "Error table incorrect number of positions (" \
|
170
|
+
+ table.size.to_s + " of " + self.get_table_size.to_s \
|
171
|
+
+ ")" if table.size != self.get_table_size
|
172
|
+
|
173
|
+
@table = table
|
174
|
+
end
|
175
|
+
|
176
|
+
def parents_assignments
|
177
|
+
parents.collect { |p| p.value }
|
178
|
+
end
|
179
|
+
|
180
|
+
# returns the number of cells that conditional probability table ( CPT )
|
181
|
+
# haves.
|
182
|
+
def get_table_size
|
183
|
+
num = @outcomes.size
|
184
|
+
@givens.each { |given| num = num * given.outcomes.size }
|
185
|
+
return num
|
186
|
+
end
|
187
|
+
|
188
|
+
# Sets a probability to the node with a node_assignment conditioned to given_assignments
|
189
|
+
# P (node = node_assignment | givens = givens_assignments) = number
|
190
|
+
def set_probability(number, node_assignment, givens_assignments)
|
191
|
+
@table[get_table_index(node_assignment, givens_assignments)] = number
|
192
|
+
end
|
193
|
+
|
194
|
+
# Returns the probability of an assignment to a node conditioned to given_assignments
|
195
|
+
# P(node = node_assignment | givens = givens_assignments)
|
196
|
+
#
|
197
|
+
# All givens_assigments must have value.
|
198
|
+
def get_probability(node_assignment = value, givens_assignments = parents_assignments)
|
199
|
+
# raise "Node must have a value and a givens_assignments, otherwise put" \
|
200
|
+
# + "them in function call" if node_assignment.nil? or givens_assignments.nil?
|
201
|
+
# if there's a cached table take the index
|
202
|
+
return @table[get_table_index(node_assignment, givens_assignments)] if @table_is_a_proc.nil? or !@table_is_a_proc
|
203
|
+
# if the value is calculated on the fly using a function instead of
|
204
|
+
# a table
|
205
|
+
return @table[node_assignment, givens_assignments]
|
206
|
+
end
|
207
|
+
|
208
|
+
# Returns the corresponding index for the probability table given
|
209
|
+
# <i>node_assignment</i> and <i>givens_assignments</i>
|
210
|
+
def get_table_index(node_assignment, givens_assignments)
|
211
|
+
x = []
|
212
|
+
indices = []
|
213
|
+
index = 0
|
214
|
+
|
215
|
+
if givens_assignments.length != @givens.length
|
216
|
+
raise "Error. Number of assignments does not match node."
|
217
|
+
end
|
218
|
+
|
219
|
+
if @givens.length > 0
|
220
|
+
# create a indices array with the position of each value of
|
221
|
+
# given assignments.
|
222
|
+
givens_assignments.length.times { |i|
|
223
|
+
assignment = givens_assignments[i]
|
224
|
+
indices[i] = @givens[i].outcomes.index(assignment)
|
225
|
+
}
|
226
|
+
|
227
|
+
# create a array with the number of possible values each
|
228
|
+
# given node and this node itself can have ( node.outcomes.length )
|
229
|
+
# plus all next nodes.
|
230
|
+
x[givens_assignments.length-1] = @outcomes.length
|
231
|
+
(givens_assignments.length-2).downto(0) { |j|
|
232
|
+
x[j] = x[j+1] * @givens[j+1].outcomes.length
|
233
|
+
}
|
234
|
+
|
235
|
+
# to get the index, sum for each assignment the
|
236
|
+
# product of each given assignment outcomes size
|
237
|
+
# by its value index.
|
238
|
+
givens_assignments.length.times { |i|
|
239
|
+
index += x[i] * indices[i]
|
240
|
+
}
|
241
|
+
end
|
242
|
+
|
243
|
+
index += @outcomes.index(node_assignment)
|
244
|
+
|
245
|
+
return index
|
246
|
+
end
|
247
|
+
|
248
|
+
|
249
|
+
end
|
@@ -0,0 +1,215 @@
|
|
1
|
+
##############################################################
|
2
|
+
#
|
3
|
+
# Inference Algorithms for Bayesian Network Library for Ruby
|
4
|
+
#
|
5
|
+
# Author: Sergio Espeja ( http://www.upf.edu/pdi/iula/sergio.espeja, sergio.espeja at gmail.com )
|
6
|
+
#
|
7
|
+
# Developed in: IULA ( http://www.iula.upf.es ) and
|
8
|
+
# in bee.com.es ( http://bee.com.es )
|
9
|
+
#
|
10
|
+
# == Current implemented algorithms
|
11
|
+
# * enumeration_ask
|
12
|
+
# * prior_sample
|
13
|
+
# * rejection_sampling
|
14
|
+
# * likelihood_weighting
|
15
|
+
#
|
16
|
+
##############################################################
|
17
|
+
class BayesNet < DirectedAdjacencyGraph
|
18
|
+
|
19
|
+
# Inference Algorithms
|
20
|
+
|
21
|
+
# ENUMERATION ASK algorithm
|
22
|
+
#
|
23
|
+
# Implementation based on: S.Russell, P.Norving, "Artificial
|
24
|
+
# Intelligence, A Modern Approach", 2nd Edition. pp 506
|
25
|
+
#
|
26
|
+
# <b>x</b> --> query variable
|
27
|
+
#
|
28
|
+
# <b>e</b> --> variables with observed values
|
29
|
+
def enumeration_ask(x,e, bn_vertices = vertices)
|
30
|
+
e << x
|
31
|
+
q = []
|
32
|
+
#p bn_vertices.collect { |v| v.name }
|
33
|
+
x.outcomes.each {|outcome|
|
34
|
+
x.set_value(outcome)
|
35
|
+
q << enumerate_all(bn_vertices, e)
|
36
|
+
}
|
37
|
+
q
|
38
|
+
end
|
39
|
+
|
40
|
+
# Returns a sample from prior joint distribution specified by the network.
|
41
|
+
#
|
42
|
+
# Implementation based on: S.Russell, P.Norving, "Artificial
|
43
|
+
# Intelligence, A Modern Approach", 2nd Edition. pp 511-512
|
44
|
+
#
|
45
|
+
# The input are the nodes of the bn ordered by dependencies see nodes_ordered_by_dependencies
|
46
|
+
def prior_sample(nodes_ordered = nodes_ordered_by_dependencies)
|
47
|
+
sample = Array.new
|
48
|
+
nodes_ordered.each { |v|
|
49
|
+
value = rand < v.get_probability(true)
|
50
|
+
v.set_value(value)
|
51
|
+
sample << v.copy
|
52
|
+
}
|
53
|
+
# leave the bn clear of values.
|
54
|
+
nodes_ordered.each { |v| v.clear_value }
|
55
|
+
|
56
|
+
return sample
|
57
|
+
end
|
58
|
+
|
59
|
+
# Returns an estimation of P(X=x|e) = <P(X=x|e), 1 - P(X=x|e)> obtained. Generates samples from prior joint
|
60
|
+
# distribution specified by the network, rejects all those that do not match the evidence,
|
61
|
+
# and finally counts hoy often X = x occurs in remaining samples.
|
62
|
+
#
|
63
|
+
# Caution, this algorthm is unusable for complex problems because rejects many samples!
|
64
|
+
#
|
65
|
+
# Implementation based on: S.Russell, P.Norving, "Artificial
|
66
|
+
# Intelligence, A Modern Approach", 2nd Edition. pp 513
|
67
|
+
#
|
68
|
+
# <b>x</b> --> query variable
|
69
|
+
#
|
70
|
+
# <b>e</b> --> variables with observed values
|
71
|
+
#
|
72
|
+
# <b>n</b> --> Number of samples generated
|
73
|
+
#
|
74
|
+
def rejection_sampling( x, e, n, bn = self )
|
75
|
+
|
76
|
+
evidece_list = [e] if e.class != Array
|
77
|
+
x_list = [x] if x.class != Array
|
78
|
+
|
79
|
+
nodes_ordered = bn.nodes_ordered_by_dependencies
|
80
|
+
evidence_vector = get_vector_value(evidece_list, nodes_ordered)
|
81
|
+
x_vector = get_vector_value(x_list, nodes_ordered)
|
82
|
+
|
83
|
+
total_valid = 0; total_correct = 0
|
84
|
+
n.times do
|
85
|
+
sample_vector = bn.prior_sample(nodes_ordered).collect {|v| v.value}
|
86
|
+
|
87
|
+
valid = true; correct = true
|
88
|
+
for i in 0..(sample_vector.size-1) do
|
89
|
+
correct = false if !x_vector[i].nil? and sample_vector[i] != x_vector[i]
|
90
|
+
valid = false and break if !evidence_vector[i].nil? and sample_vector[i] != evidence_vector[i]
|
91
|
+
end
|
92
|
+
|
93
|
+
next if !valid
|
94
|
+
total_valid += 1
|
95
|
+
total_correct += 1 if correct
|
96
|
+
end
|
97
|
+
|
98
|
+
p_true = total_correct.to_f/total_valid.to_f
|
99
|
+
return [p_true, 1-p_true]
|
100
|
+
#return [total_correct.to_f, total_valid.to_f]
|
101
|
+
end
|
102
|
+
|
103
|
+
# Returns an estimation of P(X=x|e) = <P(X=x|e), 1 - P(X=x|e)> obtained.
|
104
|
+
#
|
105
|
+
# Implementation based on: S.Russell, P.Norving, "Artificial
|
106
|
+
# Intelligence, A Modern Approach", 2nd Edition. pp 515
|
107
|
+
#
|
108
|
+
# <b>x</b> --> query variable
|
109
|
+
#
|
110
|
+
# <b>e</b> --> variables with observed values
|
111
|
+
#
|
112
|
+
# <b>n</b> --> Number of samples generated
|
113
|
+
#
|
114
|
+
def likelihood_weighting( x, e, n, bn = self )
|
115
|
+
|
116
|
+
retval = [0.0, 0.0]
|
117
|
+
n.times {
|
118
|
+
w_sample, w = weighted_sample(e)
|
119
|
+
value = w_sample.select { |v| v.name == x.name }[0].value
|
120
|
+
#p value
|
121
|
+
if value == x.value
|
122
|
+
retval[1] += w
|
123
|
+
else
|
124
|
+
retval[0] += w
|
125
|
+
end
|
126
|
+
}
|
127
|
+
|
128
|
+
norm = retval[1].to_f / (retval[0]+retval[1]).to_f
|
129
|
+
|
130
|
+
return [norm, 1-norm]
|
131
|
+
end
|
132
|
+
|
133
|
+
protected
|
134
|
+
# Auxiliar function to compute Enumeration Ask Algorithm
|
135
|
+
def enumerate_all(vars, e)
|
136
|
+
|
137
|
+
return 1.0 if vars.empty?
|
138
|
+
|
139
|
+
y = vars.first; i = 1
|
140
|
+
while !y.all_parents_with_values? and i < vars.size
|
141
|
+
y = vars[i]
|
142
|
+
i = i + 1
|
143
|
+
end
|
144
|
+
raise "Error bayes net not computable with enumeration-ask " + \
|
145
|
+
"algorithm" if i == vars.size and !y.all_parents_with_values?
|
146
|
+
|
147
|
+
if e.include?(y)
|
148
|
+
return p_v_cond_parents(y) * enumerate_all(vars-[y], e)
|
149
|
+
else
|
150
|
+
prob = 0.0
|
151
|
+
y.outcomes.each { |outcome|
|
152
|
+
y.set_value(outcome)
|
153
|
+
prob = prob + p_v_cond_parents(y) * enumerate_all(vars-[y], e+[y])
|
154
|
+
y.clear_value
|
155
|
+
}
|
156
|
+
return prob
|
157
|
+
end
|
158
|
+
end
|
159
|
+
|
160
|
+
# Returns an event and a weight.
|
161
|
+
#
|
162
|
+
# Implementation based on: S.Russell, P.Norving, "Artificial
|
163
|
+
# Intelligence, A Modern Approach", 2nd Edition. pp 515
|
164
|
+
#
|
165
|
+
# <b>e</b> --> variables with observed values
|
166
|
+
#
|
167
|
+
def weighted_sample(e, bn = self)
|
168
|
+
|
169
|
+
nodes_ordered = bn.nodes_ordered_by_dependencies
|
170
|
+
|
171
|
+
sample = Array.new
|
172
|
+
w = 1.0
|
173
|
+
nodes_ordered.each { |v|
|
174
|
+
node_actual = e.select { |node| node.name == v.name } if e.class == Array
|
175
|
+
node_actual = [e] if e.class == BayesNetNode and e.name == v.name
|
176
|
+
if !node_actual.nil? and node_actual.size == 1
|
177
|
+
value = node_actual[0].value
|
178
|
+
w = w * v.get_probability(value)
|
179
|
+
else
|
180
|
+
value = rand < v.get_probability(true)
|
181
|
+
end
|
182
|
+
v.set_value(value)
|
183
|
+
sample << v.copy
|
184
|
+
}
|
185
|
+
|
186
|
+
# leave the bn clear of values.
|
187
|
+
bn.clear_values!
|
188
|
+
return sample, w
|
189
|
+
end
|
190
|
+
|
191
|
+
# Axiliar function that returns an array of bn_vertices_ordered.size positions
|
192
|
+
# with nil if position aren't in vertices_vector, and value if there's a match
|
193
|
+
# in vertices_vector.
|
194
|
+
def get_vector_value(vertices_vector, bn_vertices_ordered)
|
195
|
+
bn_vertices_ordered.collect { |v|
|
196
|
+
if !vertices_vector.nil?
|
197
|
+
node_actual = vertices_vector.select { |node| node.name == v.name }
|
198
|
+
|
199
|
+
case node_actual.size
|
200
|
+
when 0
|
201
|
+
nil
|
202
|
+
when 1
|
203
|
+
node_actual[0].value
|
204
|
+
else
|
205
|
+
raise "Error in get_vector_value"
|
206
|
+
end
|
207
|
+
else
|
208
|
+
nil
|
209
|
+
end
|
210
|
+
}
|
211
|
+
end
|
212
|
+
|
213
|
+
|
214
|
+
|
215
|
+
end
|
@@ -0,0 +1,65 @@
|
|
1
|
+
##############################################################
|
2
|
+
#
|
3
|
+
# Methods for poulating Bayes Net Probabilities tables.
|
4
|
+
#
|
5
|
+
# Author: Sergio Espeja ( http://www.upf.edu/pdi/iula/sergio.espeja, sergio.espeja at gmail.com )
|
6
|
+
#
|
7
|
+
# Developed in: IULA ( http://www.iula.upf.es )
|
8
|
+
#
|
9
|
+
##############################################################
|
10
|
+
|
11
|
+
|
12
|
+
# Bayes Net Table Probabilities Generator
|
13
|
+
class BnTableProbabilitiesGenerator
|
14
|
+
def get_node_probability_from_boolean_combination(boolean_combination)
|
15
|
+
0.0
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
# Bayes Net Table Probabilities Generator from Positive Negative Relations
|
20
|
+
class BNTPGFromPositiveNegativeRelations < BnTableProbabilitiesGenerator
|
21
|
+
|
22
|
+
public
|
23
|
+
# type_of_position_impact is a array of boolean values showing the
|
24
|
+
# relation ( positive | negative ) beetwen the node and its parents.
|
25
|
+
def table_probabilities_for_node(node, type_of_position_impact)
|
26
|
+
raise "Node parents and type_of_position_impact with different size" if node.parents.size != type_of_position_impact.size
|
27
|
+
boolean_combinations = []
|
28
|
+
(2**node.parents.size).times { |i|
|
29
|
+
boolean_combination = Array.new(node.parents.size, false)
|
30
|
+
actual_value = i
|
31
|
+
(node.parents.size).times { |j|
|
32
|
+
boolean_combination[j] = !(actual_value%2 == 0)
|
33
|
+
actual_value = actual_value / 2
|
34
|
+
}
|
35
|
+
boolean_combinations << boolean_combination
|
36
|
+
}
|
37
|
+
#p boolean_combinations
|
38
|
+
table_probabilities = [] # Array.new(2**(node.parents.size+1))
|
39
|
+
boolean_combinations.each { |boolean_combination|
|
40
|
+
[true,false].each { |node_value|
|
41
|
+
prob = BNTPGFromPositiveNegativeRelations.get_node_probability_from_boolean_combination(boolean_combination, type_of_position_impact)
|
42
|
+
prob = 1 - prob if node_value == false
|
43
|
+
table_probabilities[node.get_table_index(node_value, boolean_combination)] = prob
|
44
|
+
#p "pos :" + node.get_table_index(node_value, boolean_combination).to_s
|
45
|
+
#p "Ok :" + node.get_table_index(node_value, boolean_combination).to_s if node.get_table_index(node_value, boolean_combination) > 2**node.parents.size
|
46
|
+
}
|
47
|
+
}
|
48
|
+
#p table_probabilities
|
49
|
+
table_probabilities
|
50
|
+
end
|
51
|
+
|
52
|
+
# returns P(node=yes| parents={boolean_combination}) where parents have
|
53
|
+
# relations with the node showed in type_of_position_impact
|
54
|
+
# type_of_position_impact is a array of boolean values showing the
|
55
|
+
# relation ( positive | negative ) beetwen the node and its parents.
|
56
|
+
def self.get_node_probability_from_boolean_combination(boolean_combination, type_of_position_impact)
|
57
|
+
num_eq = 0.0
|
58
|
+
boolean_combination.size.times { |i|
|
59
|
+
num_eq = num_eq + 1.0 if type_of_position_impact[i] && boolean_combination[i]
|
60
|
+
num_eq = num_eq + 1.0 if !type_of_position_impact[i] && !boolean_combination[i]
|
61
|
+
}
|
62
|
+
num_eq / boolean_combination.size.to_f
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
data/lib/bn4r/version.rb
ADDED
data/lib/bn4r.rb
ADDED
@@ -0,0 +1,11 @@
|
|
1
|
+
##############################################################
|
2
|
+
#
|
3
|
+
# Bayesian Network Library for Ruby
|
4
|
+
#
|
5
|
+
# Author: Sergio Espeja ( sergio.espeja@gmail.com )
|
6
|
+
#
|
7
|
+
# Developed in: IULA ( http://www.iula.upf.es ) and
|
8
|
+
# in bee.com.es ( http://bee.com.es )
|
9
|
+
#
|
10
|
+
##############################################################
|
11
|
+
Dir[File.join(File.dirname(__FILE__), 'bn4r/**/*.rb')].sort.each { |lib| require lib }
|
data/test/bn4r_test.rb
ADDED
@@ -0,0 +1,290 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/test_helper.rb'
|
2
|
+
|
3
|
+
class Bn4rTest < Test::Unit::TestCase
|
4
|
+
|
5
|
+
# Returns the BayesNet used as example in "Artificial Intelligence A
|
6
|
+
# Modern Approach, Rusell & Norvig, 2nd Ed." pp.494
|
7
|
+
def self.bayes_net_aima
|
8
|
+
bn_aima = BayesNet.new
|
9
|
+
|
10
|
+
b = BayesNetNode.new("Burglary")
|
11
|
+
e = BayesNetNode.new("Earthquake")
|
12
|
+
a = BayesNetNode.new("Alarm")
|
13
|
+
j = BayesNetNode.new("JohnCalls")
|
14
|
+
m = BayesNetNode.new("MaryCalls")
|
15
|
+
|
16
|
+
bn_aima.add_vertex(b)
|
17
|
+
bn_aima.add_vertex(e)
|
18
|
+
bn_aima.add_vertex(a)
|
19
|
+
bn_aima.add_vertex(j)
|
20
|
+
bn_aima.add_vertex(m)
|
21
|
+
|
22
|
+
bn_aima.add_edge(b,a)
|
23
|
+
bn_aima.add_edge(e,a)
|
24
|
+
bn_aima.add_edge(a,j)
|
25
|
+
bn_aima.add_edge(a,m)
|
26
|
+
|
27
|
+
b.set_probability_table([], [0.001, 0.999] )
|
28
|
+
e.set_probability_table([], [0.002, 0.998] )
|
29
|
+
|
30
|
+
a.set_probability_table([b,e], [0.95, 0.05, 0.94, 0.06, 0.29, 0.61, 0.001,0.999] )
|
31
|
+
|
32
|
+
j.set_probability_table([a], [0.90,0.10,0.05,0.95])
|
33
|
+
m.set_probability_table([a], [0.70,0.30,0.01,0.99])
|
34
|
+
|
35
|
+
bn_aima
|
36
|
+
end
|
37
|
+
|
38
|
+
# Returns the BayesNet used as example in "Artificial Intelligence A
|
39
|
+
# Modern Approach, Rusell & Norvig, 2nd Ed." pp.510
|
40
|
+
def self.bayes_net_aima2
|
41
|
+
bn_aima = BayesNet.new
|
42
|
+
|
43
|
+
cloudy = BayesNetNode.new("Cloudy")
|
44
|
+
sprinkler = BayesNetNode.new("Sprinkler")
|
45
|
+
rain = BayesNetNode.new("Rain")
|
46
|
+
wetgrass = BayesNetNode.new("WetGrass")
|
47
|
+
|
48
|
+
bn_aima.add_vertex(cloudy)
|
49
|
+
bn_aima.add_vertex(sprinkler)
|
50
|
+
bn_aima.add_vertex(rain)
|
51
|
+
bn_aima.add_vertex(wetgrass)
|
52
|
+
|
53
|
+
bn_aima.add_edge(cloudy,sprinkler)
|
54
|
+
bn_aima.add_edge(cloudy,rain)
|
55
|
+
bn_aima.add_edge(sprinkler,wetgrass)
|
56
|
+
bn_aima.add_edge(rain,wetgrass)
|
57
|
+
|
58
|
+
cloudy.set_probability_table([], [0.5, 0.5] )
|
59
|
+
|
60
|
+
sprinkler.set_probability_table([cloudy], [0.1, 0.9, 0.5, 0.5] )
|
61
|
+
rain.set_probability_table([cloudy], [0.8, 0.2, 0.2, 0.8] )
|
62
|
+
|
63
|
+
wetgrass.set_probability_table([sprinkler, rain], [0.99, 0.01, 0.9, 0.1, 0.9, 0.1, 0.0, 1.0] )
|
64
|
+
bn_aima
|
65
|
+
end
|
66
|
+
|
67
|
+
def bayes_net_aaile
|
68
|
+
bn = BayesNet.new
|
69
|
+
rel = BayesNetNode.new("relational")
|
70
|
+
q = BayesNetNode.new("qualificative")
|
71
|
+
a = BayesNetNode.new("Adverbial")
|
72
|
+
bn.add_vertex(rel)
|
73
|
+
bn.add_vertex(q)
|
74
|
+
bn.add_vertex(a)
|
75
|
+
|
76
|
+
# ["preN", ...]
|
77
|
+
preN = BayesNetNode.new("preN")
|
78
|
+
bn.add_vertex(preN)
|
79
|
+
|
80
|
+
postN = BayesNetNode.new("postN")
|
81
|
+
bn.add_vertex(postN)
|
82
|
+
|
83
|
+
# bn.add_edge("ser")
|
84
|
+
# bn.add_edge("G")
|
85
|
+
# bn.add_edge("prep")
|
86
|
+
|
87
|
+
bn.add_edge(rel, preN)
|
88
|
+
bn.add_edge(q, preN)
|
89
|
+
bn.add_edge(a, preN)
|
90
|
+
bn.add_edge(q, postN)
|
91
|
+
|
92
|
+
bn
|
93
|
+
end
|
94
|
+
|
95
|
+
def setup
|
96
|
+
end
|
97
|
+
|
98
|
+
# TESTS
|
99
|
+
|
100
|
+
def test_create_sample_bn
|
101
|
+
bn = bayes_net_aaile
|
102
|
+
|
103
|
+
assert_equal bn.vertices.size, 5
|
104
|
+
assert_equal bn.edges.size, 4
|
105
|
+
end
|
106
|
+
|
107
|
+
|
108
|
+
def test_graph_viz
|
109
|
+
bn = bayes_net_aaile
|
110
|
+
# print "\n\n" + bn.to_dot_graph.to_s
|
111
|
+
|
112
|
+
assert bn.to_dot_graph.to_s.size > 0, "bn.to_dot_graph gives no output."
|
113
|
+
end
|
114
|
+
|
115
|
+
def test_probability_assingment
|
116
|
+
bn_aima = Bn4rTest.bayes_net_aima
|
117
|
+
b = bn_aima.get_variable("Burglary")
|
118
|
+
a = bn_aima.get_variable("Alarm")
|
119
|
+
j = bn_aima.get_variable("JohnCalls")
|
120
|
+
m = bn_aima.get_variable("MaryCalls")
|
121
|
+
|
122
|
+
assert_equal b.get_probability(true, []), 0.001
|
123
|
+
assert_equal b.get_probability(false, []), 0.999
|
124
|
+
assert_equal b.get_probability(false, []), 0.999
|
125
|
+
|
126
|
+
assert_equal a.get_probability(true, [false,false]), 0.001
|
127
|
+
assert_equal a.get_probability(true, [true,false]), 0.94
|
128
|
+
assert_equal a.get_probability(false, [true,true]), 0.05
|
129
|
+
|
130
|
+
assert_equal j.get_probability(true, [true]), 0.90
|
131
|
+
assert_equal m.get_probability(true, [false]), 0.01
|
132
|
+
|
133
|
+
end
|
134
|
+
|
135
|
+
def test_CPT_size
|
136
|
+
bn_aima = Bn4rTest.bayes_net_aima
|
137
|
+
b = bn_aima.get_variable("Burglary")
|
138
|
+
a = bn_aima.get_variable("Alarm")
|
139
|
+
j = bn_aima.get_variable("JohnCalls")
|
140
|
+
m = bn_aima.get_variable("MaryCalls")
|
141
|
+
|
142
|
+
assert_equal b.get_table_size, 2
|
143
|
+
assert_equal a.get_table_size, 8
|
144
|
+
assert_equal j.get_table_size, 4
|
145
|
+
assert_equal m.get_table_size, 4
|
146
|
+
end
|
147
|
+
|
148
|
+
def test_base_methods
|
149
|
+
bn_aima = Bn4rTest.bayes_net_aima
|
150
|
+
b = bn_aima.get_variable("Burglary")
|
151
|
+
a = bn_aima.get_variable("Alarm")
|
152
|
+
assert bn_aima.root?(b)
|
153
|
+
assert !bn_aima.root?(a)
|
154
|
+
|
155
|
+
end
|
156
|
+
|
157
|
+
def test_inference_by_enumeration
|
158
|
+
bn_aima = Bn4rTest.bayes_net_aima
|
159
|
+
b = bn_aima.get_variable("Burglary")
|
160
|
+
e = bn_aima.get_variable("Earthquake")
|
161
|
+
a = bn_aima.get_variable("Alarm")
|
162
|
+
j = bn_aima.get_variable("JohnCalls")
|
163
|
+
m = bn_aima.get_variable("MaryCalls")
|
164
|
+
|
165
|
+
assert_equal bn_aima.vertices.size, 5
|
166
|
+
assert_equal bn_aima.edges.size, 4
|
167
|
+
|
168
|
+
assert !bn_aima.all_nodes_with_values?
|
169
|
+
b.set_value(false); assert !bn_aima.all_nodes_with_values?
|
170
|
+
e.set_value(false); assert !bn_aima.all_nodes_with_values?
|
171
|
+
a.set_value(true); assert !bn_aima.all_nodes_with_values?
|
172
|
+
j.set_value(true); assert !bn_aima.all_nodes_with_values?
|
173
|
+
m.set_value(true)
|
174
|
+
|
175
|
+
assert bn_aima.all_nodes_with_values?
|
176
|
+
|
177
|
+
value = bn_aima.inference_by_enumeration
|
178
|
+
assert_in_delta 0.0006281112, value, 10**(-10)
|
179
|
+
|
180
|
+
bn_aima.clear_values!
|
181
|
+
assert !bn_aima.all_nodes_with_values?
|
182
|
+
|
183
|
+
end
|
184
|
+
|
185
|
+
def test_inference_by_enumeration_ask
|
186
|
+
bn_aima = Bn4rTest.bayes_net_aima
|
187
|
+
b = bn_aima.get_variable("Burglary")
|
188
|
+
e = bn_aima.get_variable("Earthquake")
|
189
|
+
a = bn_aima.get_variable("Alarm")
|
190
|
+
j = bn_aima.get_variable("JohnCalls")
|
191
|
+
m = bn_aima.get_variable("MaryCalls")
|
192
|
+
j.set_value(true)
|
193
|
+
m.set_value(true)
|
194
|
+
|
195
|
+
assert_equal bn_aima.vertices.size, 5
|
196
|
+
assert_equal bn_aima.edges.size, 4
|
197
|
+
value1 = bn_aima.enumeration_ask(b,[j,m])
|
198
|
+
value2 = bn_aima.enumeration_ask(b,[j,m], [j,m,a,b,e])
|
199
|
+
value3 = bn_aima.enumeration_ask(b,[j,m], [b,e,a,j,m])
|
200
|
+
assert_equal value1[0], value2[0]
|
201
|
+
assert_equal value1[0].to_f, value3[0].to_f
|
202
|
+
assert_in_delta value1[1].to_f, value2[1].to_f, 10**(-10)
|
203
|
+
assert_equal value1[1], value3[1]
|
204
|
+
assert_in_delta 0.000592242, bn_aima.enumeration_ask(b,[j,m])[0], 10**(-9)
|
205
|
+
bn_aima.clear_values!
|
206
|
+
assert !bn_aima.all_nodes_with_values?
|
207
|
+
|
208
|
+
end
|
209
|
+
|
210
|
+
|
211
|
+
def test_table_probabilities_for_node
|
212
|
+
bn_aima = Bn4rTest.bayes_net_aima
|
213
|
+
b = bn_aima.get_variable("Burglary")
|
214
|
+
e = bn_aima.get_variable("Earthquake")
|
215
|
+
a = bn_aima.get_variable("Alarm")
|
216
|
+
BNTPGFromPositiveNegativeRelations.new.table_probabilities_for_node(a, [true,true])
|
217
|
+
|
218
|
+
# BNTPGFromPositiveNegativeRelations.new.populate_bn_with_tags(bn_aima)
|
219
|
+
assert true
|
220
|
+
end
|
221
|
+
|
222
|
+
def test_prior_sampling
|
223
|
+
|
224
|
+
bn = Bn4rTest.bayes_net_aima2
|
225
|
+
|
226
|
+
hash = Hash.new(0)
|
227
|
+
nodes_ordered = bn.nodes_ordered_by_dependencies
|
228
|
+
10000.times { hash[ bn.prior_sample(nodes_ordered).collect {|v| v.value} ] += 1 }
|
229
|
+
|
230
|
+
combination_of_prob = nodes_ordered.collect { |v|
|
231
|
+
case v.name
|
232
|
+
when "Cloudy"
|
233
|
+
true
|
234
|
+
when "Sprinkler"
|
235
|
+
false
|
236
|
+
when "Rain"
|
237
|
+
true
|
238
|
+
when "WetGrass"
|
239
|
+
true
|
240
|
+
else
|
241
|
+
raise "Incorrect BayesNet created at Bn4rTest.bayes_net_aima2"
|
242
|
+
end
|
243
|
+
}
|
244
|
+
|
245
|
+
prob = hash[combination_of_prob].to_f/10000.0
|
246
|
+
|
247
|
+
assert_in_delta 0.3, prob, 0.1, "Its inprobable but possible that this error occurs, \
|
248
|
+
because we are working with statistics and random data, \
|
249
|
+
try again and if still occurs take care of it."
|
250
|
+
|
251
|
+
end
|
252
|
+
|
253
|
+
def test_rejection_sampling
|
254
|
+
bn = Bn4rTest.bayes_net_aima2
|
255
|
+
|
256
|
+
rain = bn.get_variable("Rain").copy
|
257
|
+
sprinkler = bn.get_variable("Sprinkler").copy
|
258
|
+
|
259
|
+
rain.set_value(true)
|
260
|
+
sprinkler.set_value(true)
|
261
|
+
|
262
|
+
results = bn.rejection_sampling(rain, sprinkler, 1000)
|
263
|
+
|
264
|
+
str_error = " Its improbable but possible that this error occurs, \
|
265
|
+
because we are working with statistics and random data, \
|
266
|
+
try again and if still occurs take care of it."
|
267
|
+
assert ((results[0] > 0.2) and (results[0] < 0.4)), "Results aren't in interval [0.2,0.4] --> " + results[0].to_s + str_error
|
268
|
+
assert ((results[1] > 0.6) and (results[1] < 0.8)), "Results aren't in interval [0.6,0.8] --> " + results[1].to_s + str_error
|
269
|
+
end
|
270
|
+
|
271
|
+
def test_likelihood_weighting
|
272
|
+
bn = Bn4rTest.bayes_net_aima2
|
273
|
+
|
274
|
+
rain = bn.get_variable("Rain").copy
|
275
|
+
sprinkler = bn.get_variable("Sprinkler").copy
|
276
|
+
|
277
|
+
rain.set_value(true)
|
278
|
+
sprinkler.set_value(true)
|
279
|
+
|
280
|
+
results = bn.likelihood_weighting(rain, sprinkler, 100)
|
281
|
+
|
282
|
+
str_error = " Its improbable but possible that this error occurs, \
|
283
|
+
because we are working with statistics and random data, \
|
284
|
+
try again and if still occurs take care of it."
|
285
|
+
assert ((results[0] > 0.2) and (results[0] < 0.4)), "Results aren't in interval [0.2,0.4] --> " + results[0].to_s + str_error
|
286
|
+
assert ((results[1] > 0.6) and (results[1] < 0.8)), "Results aren't in interval [0.6,0.8] --> " + results[1].to_s + str_error
|
287
|
+
end
|
288
|
+
|
289
|
+
|
290
|
+
end
|
data/test/test_helper.rb
ADDED
metadata
ADDED
@@ -0,0 +1,74 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
rubygems_version: 0.8.11
|
3
|
+
specification_version: 1
|
4
|
+
name: bn4r
|
5
|
+
version: !ruby/object:Gem::Version
|
6
|
+
version: 0.1.0
|
7
|
+
date: 2006-10-31 00:00:00 +01:00
|
8
|
+
summary: bn4r is a bayesian networks library on ruby that provides the user with classes for create bayesian networks and diverse algorithms for solve them.
|
9
|
+
require_paths:
|
10
|
+
- lib
|
11
|
+
email: sergio.espeja@gmail.com
|
12
|
+
homepage: http://bn4r.rubyforge.org
|
13
|
+
rubyforge_project: bn4r
|
14
|
+
description: bn4r is a bayesian networks library on ruby that provides the user with classes for create bayesian networks and diverse algorithms for solve them.
|
15
|
+
autorequire: bn4r
|
16
|
+
default_executable:
|
17
|
+
bindir: bin
|
18
|
+
has_rdoc: true
|
19
|
+
required_ruby_version: !ruby/object:Gem::Version::Requirement
|
20
|
+
requirements:
|
21
|
+
- - ">"
|
22
|
+
- !ruby/object:Gem::Version
|
23
|
+
version: 0.0.0
|
24
|
+
version:
|
25
|
+
platform: ruby
|
26
|
+
signing_key:
|
27
|
+
cert_chain:
|
28
|
+
authors:
|
29
|
+
- Sergio Espeja
|
30
|
+
files:
|
31
|
+
- README
|
32
|
+
- CHANGELOG
|
33
|
+
- Rakefile
|
34
|
+
- test/test_helper.rb
|
35
|
+
- test/bn4r_test.rb
|
36
|
+
- lib/bn4r
|
37
|
+
- lib/bn4r.rb
|
38
|
+
- lib/bn4r/version.rb
|
39
|
+
- lib/bn4r/bn.rb
|
40
|
+
- lib/bn4r/bn_table_probabilities.rb
|
41
|
+
- lib/bn4r/bn_algorithms.rb
|
42
|
+
test_files: []
|
43
|
+
|
44
|
+
rdoc_options:
|
45
|
+
- --quiet
|
46
|
+
- --title
|
47
|
+
- bn4r documentation
|
48
|
+
- --opname
|
49
|
+
- index.html
|
50
|
+
- --line-numbers
|
51
|
+
- --main
|
52
|
+
- README
|
53
|
+
- --inline-source
|
54
|
+
- --exclude
|
55
|
+
- ^(examples|extras)/
|
56
|
+
extra_rdoc_files:
|
57
|
+
- README
|
58
|
+
- CHANGELOG
|
59
|
+
executables: []
|
60
|
+
|
61
|
+
extensions: []
|
62
|
+
|
63
|
+
requirements: []
|
64
|
+
|
65
|
+
dependencies:
|
66
|
+
- !ruby/object:Gem::Dependency
|
67
|
+
name: rgl
|
68
|
+
version_requirement:
|
69
|
+
version_requirements: !ruby/object:Gem::Version::Requirement
|
70
|
+
requirements:
|
71
|
+
- - ">="
|
72
|
+
- !ruby/object:Gem::Version
|
73
|
+
version: 0.2.3
|
74
|
+
version:
|