def testAll(self): """ this is actually the Water-sprinkler example """ c = DiscretePotential(['c'], [2], [0.5,0.5]) # Pr(C) s = DiscretePotential(['s','c'], [2,2], [0.5,0.9,0.5,0.1]) # Pr(S|C) r = DiscretePotential(['r','c'], [2,2], [0.8,0.2,0.2,0.8]) # Pr(R|C) w = DiscretePotential(['w','s','r'], [2,2,2]) # Pr(W|S,R) w[:,0,0] = [0.99, 0.01] w[:,0,1] = [0.1, 0.9] w[:,1,0] = [0.1, 0.9] w[:,1,1] = [0.0, 1.0] cr = c * r # Pr(C,R) = Pr(R|C) * Pr(C) crs = cr * s # Pr(C,S,R) = Pr(S|C) * Pr(C,R) print crs, crs.names_list print crs[:,0,0] crsw = crs * w # Pr(C,S,R,W) = Pr(W|S,R) * Pr(C,R,S) # this can be verified using any bayesian network software # check the result for the multiplication and marginalisation assert(na.allclose(crsw.Marginalise('s r w'.split()).cpt, [0.5,0.5]) and \ na.allclose(crsw.Marginalise('c r w'.split()).cpt, [0.7,0.3]) and \ na.allclose(crsw.Marginalise('c s w'.split()).cpt, [0.5,0.5]) and \ na.allclose(crsw.Marginalise('c s r'.split()).cpt, [0.349099,0.6509])), \ "Something's wrong on the big Test..."
def testSample(self): ## cCPT = distributions.MultinomialDistribution(self.c) ## sCPT = distributions.MultinomialDistribution(self.s) ## rCPT = distributions.MultinomialDistribution(self.r) ## wCPT = distributions.MultinomialDistribution(self.w) cCPT = self.c.distribution sCPT = self.s.distribution rCPT = self.r.distribution wCPT = self.w.distribution cCPT.initializeCounts() sCPT.initializeCounts() rCPT.initializeCounts() wCPT.initializeCounts() for i in range(1000): sample = self.BNet.Sample()[0] # Can use sample in all of these, it will ignore extra variables cCPT.incrCounts(sample) sCPT.incrCounts(sample) rCPT.incrCounts(sample) wCPT.incrCounts(sample) ## cCPT[sample] += 1 ## sCPT[sample] += 1 ## rCPT[sample] += 1 ## wCPT[sample] += 1 assert(na.allclose(cCPT,self.c.distribution.cpt,atol=.1) and \ na.allclose(sCPT,self.s.distribution.cpt,atol=.1) and \ na.allclose(rCPT,self.r.distribution.cpt,atol=.1) and \ na.allclose(wCPT,self.w.distribution.cpt,atol=.1)), \ "Samples did not generate appropriate CPTs"
def testLearning(self): """ Sample network and then learn parameters and check that they are relatively close to original. """ ev = self.engine.BNet.Sample(n=10000) #Remember what the old CPTs looked like and keep track of original dimension order cCPT = self.c.distribution.cpt.copy() self.c.distribution.isAdjustable=True self.c.distribution.uniform() sCPT = self.s.distribution.cpt.copy() self.s.distribution.isAdjustable=True self.s.distribution.uniform() rCPT = self.r.distribution.cpt.copy() self.r.distribution.isAdjustable=True self.r.distribution.uniform() wCPT = self.w.distribution.cpt.copy() self.w.distribution.isAdjustable=True self.w.distribution.uniform() # Learn parameters self.engine.LearnMLParams(ev) # Check that they match original parameters assert(na.allclose(cCPT,self.c.distribution.cpt,atol=.1) and \ na.allclose(sCPT,self.s.distribution.cpt,atol=.1) and \ na.allclose(rCPT,self.r.distribution.cpt,atol=.1) and \ na.allclose(wCPT,self.w.distribution.cpt,atol=.1)),\ "CPTs were more than atol=.1 apart"
def testDistributions(self): ' test the distributions' r = self.G.v['Rain'] s = self.G.v['Sprinkler'] w = self.G.v['Watson'] h = self.G.v['Holmes'] assert(allclose(r.distribution.cpt, [0.2, 0.8]) and \ allclose(s.distribution.cpt, [0.1, 0.9]) and \ allclose(w.distribution[{'Rain':1}], [0.2, 0.8]) ), \ " Distribution values are not correct"
def testInit(self): a = self.a b = self.b assert(a.g == 0.0 and \ na.allclose(a.h, na.zeros(3)) and \ na.allclose(a.K, na.zeros(shape=(3,3)))), \ " Error with standard initialization " assert(b.g == 2.0 and \ na.allclose(b.h, na.array([1,2,3], type='Float32')) and \ na.allclose(b.K, na.arange(9,shape=(3,3), type='Float32'))), \ " Error with standard initialization with parameter setting"
def testGeneral(self): """ Check that the overall algorithm works """ c = self.engine.Marginalise('c') r = self.engine.Marginalise('r') s = self.engine.Marginalise('s') w = self.engine.Marginalise('w') assert(na.allclose(c.cpt, [0.5, 0.5]) and \ na.allclose(r.cpt, [0.5, 0.5]) and \ na.allclose(s.cpt, [0.7, 0.3]) and \ na.allclose(w.cpt, [0.34909999, 0.65090001])), \ " Somethings wrong with JoinTree inference engine"
def testInference(self): """ Loads the RainWatson BN, performs inference and checks the results """ self.engine = JoinTree(self.G) r=self.engine.Marginalise('Rain') s=self.engine.Marginalise('Sprinkler') w=self.engine.Marginalise('Watson') h=self.engine.Marginalise('Holmes') assert(allclose(r.cpt,[0.2,0.8]) and \ allclose(s.cpt,[0.1,0.9]) and \ allclose(w.cpt,[0.36,0.64]) and \ allclose(h.cpt,[ 0.272, 0.728]) ), \ " Somethings wrong with JoinTree inference engine"
def testObservedDiscrete(self): """ DISCRETE: Compute and check the probability of water-sprinkler given some evidence """ self.engine.SetObs({'c':1,'s':0}) res = self.engine.MarginaliseAll() cprob, sprob, rprob, wprob = res['c'], res['s'], res['r'], res['w'] error = 0.05 assert(na.allclose(cprob.cpt,[0.0,1.0], atol=error) and \ na.allclose(rprob.cpt,[0.2,0.8], atol=error) and \ na.allclose(sprob.cpt,[1.0,0.0], atol=error) and \ na.allclose(wprob.cpt,[ 0.278, 0.722], atol=error) ), \ " Somethings wrong with MCMC inference with evidence "
def testEvidence(self): """ check that evidence works """ print 'evidence c=1,s=0' self.engine.SetObs({'c':1,'s':0}) c = self.engine.Marginalise('c') r = self.engine.Marginalise('r') s = self.engine.Marginalise('s') w = self.engine.Marginalise('w') assert(na.allclose(c.cpt,[0.0,1.0]) and \ na.allclose(r.cpt,[0.2,0.8]) and \ na.allclose(s.cpt,[1.0,0.0]) and \ na.allclose(w.cpt,[ 0.278, 0.722]) ), \ " Somethings wrong with JoinTree evidence"
def testUnobservedDiscrete(self): """ DISCRETE: Compute and check the probability of water-sprinkler given no evidence """ cprob, rprob, sprob, wprob = self.engine.MarginaliseAll() error = 0.05 #print cprob[True] <= (0.5 + error)and cprob[True] >= (0.5-error) #print wprob[True] <= (0.65090001 + 2*error) and wprob[True] >= (0.65090001 - 2*error) #print sprob[True] <= (0.3 + error) and sprob[True] >= (0.3 - error) assert(na.allclose(cprob[True], 0.5, atol = error) and \ na.allclose(sprob[True], 0.3, atol = error) and \ na.allclose(rprob[True], 0.5, atol = error) and \ na.allclose(wprob[True], 0.6509, atol = error)), \ "Incorrect probability with unobserved water-sprinkler network"
def testEM(self): # sample the network 2000 times cases = self.BNet.Sample(2000) # delete some observations for i in range(500): case = cases[3*i] rand = random.sample(['c','s','r','w'],1)[0] case[rand] = '?' for i in range(50): case = cases[3*i] rand = random.sample(['c','s','r','w'],1)[0] case[rand] = '?' # create a new BNet with same nodes as self.BNet but all parameters # set to 1s G = copy.deepcopy(self.BNet) G.InitDistributions() engine = EMLearningEngine(G) engine.EMLearning(cases, 10) tol = 0.08 assert(na.alltrue([na.allclose(v.distribution.cpt, self.BNet.v[v.name].distribution.cpt, atol=tol) \ for v in engine.BNet.all_v])), \ " Learning does not converge to true values " print 'ok!!!!!!!!!!!!'
def __eq__(a,b): """ True if a and b have same elements, size and names """ if b.__class__ == na.NumArray: # in case b is a just a numarray and not a Table instance # in this case, variable should absoltely be at the same order # otherwise the Table and numArray are considered as different return (na.alltrue(a.cpt.flat == b.flat) \ and a.shape == b.shape) elif b == None: # in case b is None type return False elif isinstance(b, (int, float, long)): # b is just a number, int, float, long return a.cpt == b else: # the b class should better be a Table or something like that # order of variables is not important # put the variables in the same order # first must get the correspondance vector : bcpt = a.prepareOther(b) return (a.names == b.names and \ bcpt.shape == a.shape and \ na.allclose(bcpt, a.cpt))
def testSample(self): cCPT = distributions.MultinomialDistribution(self.c) sCPT = distributions.MultinomialDistribution(self.s) rCPT = distributions.MultinomialDistribution(self.r) wCPT = distributions.MultinomialDistribution(self.w) for i in range(1000): sample = self.BNet.Sample # Can use sample in all of these, it will ignore extra variables cCPT[sample] += 1 sCPT[sample] += 1 rCPT[sample] += 1 wCPT[sample] += 1 assert(na.allclose(cCPT,self.c.cpt,atol=.1) and \ na.allclose(sCPT,self.s.cpt,atol-.1) and \ na.allclose(rCPT,self.r.cpt,atol-.1) and \ na.allclose(wCPT,self.w.cpt,atol-.1)), \ "Samples did not generate appropriate CPTs"
def hasntConverged(self, old, new, precision): ''' Return true if the difference of distribution of at least one vertex of the old and new BNet is bigger than precision ''' if not old : return True else: return not na.alltrue([na.allclose(v.distribution, new.v[v.name].distribution, atol=precision) for v in old.v.values()])
def testSampling(self): " Test the sample() function " a = self.a.distribution a.setParameters(mu=5, sigma=1) # take 1000 samples from distribution samples = [a.sample() for i in range(1000)] # verify values b = self.a.GetSamplingDistribution() b.initializeCounts() b.incrCounts(samples) b.setCounts() error=0.05 assert(na.allclose(b.mean, a.mean, atol = error) and \ na.allclose(b.sigma, a.sigma, atol=error)), \ "Sampling does not seem to produce a gaussian distribution" gd = self.g.distribution #2 discrete parents, 2 continuous parents
def testCounting(self): " test counting of samples" a = self.a.distribution a.initializeCounts() assert(a.samples == list()), \ "Samples list not initialized correctly" a.incrCounts(range(10)) a.incrCounts(5) assert(a.samples == range(10) + [5]), \ "Elements are not added correctly to Samples list" a.setCounts() error = 0.0001 assert(na.allclose([a.mean, a.sigma], [4.545454, 2.876324], atol = error)), \ "Mu and sigma doesn't seem to be calculated correctly..."
def testML(self): # sample the network 2000 times cases = self.BNet.Sample(2000) # create a new BNet with same nodes as self.BNet but all parameters # set to 1s G = copy.deepcopy(self.BNet) G.InitDistributions() # create an infeence engine engine = JoinTree(G) # learn according to the test cases engine.LearnMLParams(cases) tol = 0.05 assert(na.alltrue([na.allclose(v.distribution.cpt, self.BNet.v[v.name].distribution.cpt, atol=tol) \ for v in G.all_v])), \ " Learning does not converge to true values "
def _isClose(a, b, *args, **kargs): return bool(na.allclose(na.ravel(a), na.ravel(b), *args, **kargs))
def testIndexing(self): fd = self.f.distribution # 2 discrete parents : d(2),e(3) fd.setParameters(mu=range(6), sigma=range(6)) # # test normal indexing # assert(na.allclose(fd[0][0].flat, na.array(range(3),type='Float32')) and \ # na.allclose(fd[0][1].flat, na.array(range(3), type='Float32')) and \ # na.allclose(fd[1,2][0].flat, na.array(5, type='Float32')) and \ # na.allclose(fd[1,2][1].flat, na.array(5, type='Float32'))), \ # "Normal indexing does not seem to work..." # test dict indexing assert(na.allclose(fd[{'d':0}][0].flat, na.array(range(3), type='Float32')) and \ na.allclose(fd[{'d':0}][1].flat, na.array(range(3), type='Float32')) and \ na.allclose(fd[{'d':1,'e':2}][0].flat, na.array(5, type='Float32')) and \ na.allclose(fd[{'d':1,'e':2}][1].flat, na.array(5, type='Float32'))), \ "Dictionary indexing does not seem to work..." # now test setting of parameters fd[{'d':1,'e':2}] = {'mean':0.5, 'sigma':0.6} fd[{'d':0}] = {'mean':[0,1.2,2.4],'sigma':[0,0.8,0.9]} na.allclose(fd[{'d':0}][0].flat, na.array([0,1.2,2.4],type='Float32')) assert(na.allclose(fd[{'d':0}][0].flat, na.array([0,1.2,2.4],type='Float32')) and \ na.allclose(fd[{'d':0}][1].flat,na.array([0,0.8,0.9],type='Float32')) and \ na.allclose(fd[{'d':1,'e':2}][0].flat, na.array(0.5, type='Float32')) and \ na.allclose(fd[{'d':1,'e':2}][1].flat, na.array(0.6, type='Float32'))), \ "Setting of values using dict does not seem to work..." # now run tests for continuous parents cd = self.c.distribution # 2 continuous parents a(1),b(2) cd[{'a':0,'b':1}] = {'weights':69} assert(na.allclose(cd[{'a':0,'b':1}][2],69.0)), \ "Indexing for continuous parents does not work"