diff --git a/primo/decision/UtilityTable.py b/primo/decision/UtilityTable.py deleted file mode 100644 index 26dcfb34f2ad44c3c954385c4dcb39df2abdb643..0000000000000000000000000000000000000000 --- a/primo/decision/UtilityTable.py +++ /dev/null @@ -1,65 +0,0 @@ -# -*- coding: utf-8 -*- - -import numpy -import operator - -class UtilityTable(object): - ''' - self.variables -- list of the parent nodes - self.table -- utility table which contains the utility - ''' - - def __init__(self): - super(UtilityTable, self).__init__() - self.table = numpy.array(0) - self.variables = [] - - def add_variable(self, variable): - self.variables.append(variable) - - ax = self.table.ndim - self.table=numpy.expand_dims(self.table,ax) - self.table=numpy.repeat(self.table,len(variable.value_range),axis = ax) - - def get_ut_index(self, node_value_pairs): - nodes, values = zip(*node_value_pairs) - index = [] - for node in self.variables: - index_in_values_list = nodes.index(node) - value = values[index_in_values_list] - index.append(node.value_range.index(value)) - return tuple(index) - - def set_utility_table(self, table, nodes): - if not set(nodes) == set(self.variables): - raise Exception("The list which should define the ordering of the variables does not match" - " the variables that this cpt depends on (plus the node itself)") - if not self.table.ndim == table.ndim: - raise Exception("The provided probability table does not have the right number of dimensions") - for d,node in enumerate(nodes): - if len(node.value_range) != table.shape[d]: - raise Exception("The size of the provided probability table does not match the number of possible values of the node "+node.name+" in dimension "+str(d)) - - self.table = table - self.variables = nodes - - def set_utility(self, value, node_value_pairs): - index = self.get_ut_index(node_value_pairs) - self.table[index]=value - - def get_utility_table(self): - return self.table - - def get_variables(self): - return self.variables - - def get_utility(self, node_value_pairs): - index = self.get_ut_index(node_value_pairs) - return self.table[index] - - def __str__(self): - return str(self.table) - - - - \ No newline at end of file diff --git a/primo/decision/__init__.py b/primo/decision/__init__.py deleted file mode 100644 index d454ced1ff6061f5b73db88b47ef25e56e225643..0000000000000000000000000000000000000000 --- a/primo/decision/__init__.py +++ /dev/null @@ -1,3 +0,0 @@ -from UtilityNode import UtilityNode -from DecisionNode import DecisionNode -from UtilityTable import UtilityTable \ No newline at end of file diff --git a/primo/decision/make_decision/__init__.py b/primo/decision/make_decision/__init__.py deleted file mode 100644 index 82eee2b8dd7b5087b2b34eba8941b80cec3eb39d..0000000000000000000000000000000000000000 --- a/primo/decision/make_decision/__init__.py +++ /dev/null @@ -1,2 +0,0 @@ -from MakeDecision import MakeDecision - diff --git a/primo/reasoning/factor.py b/primo/reasoning/factor.py new file mode 100644 index 0000000000000000000000000000000000000000..18cc9a936a5efb27275aff34a2cc9c116297f2cb --- /dev/null +++ b/primo/reasoning/factor.py @@ -0,0 +1,421 @@ +import networkx as nx + +import primo.densities + +class Factor(object): + + def __init__(self,node): + self.node = node + self.calCPD = node.get_cpd().copy() + self.cluster = set() + self.isEvidence = False + + def __str__(self): + return self.node.name + + def set_evidence(self,evd): + self.calCPD = self.node.get_cpd().copy() + self.calCPD = self.calCPD.set_evidence(evd) + self.isEvidene = True + + def clear_evidence(self): + self.calCPD = self.node.get_cpd().copy() + self.isEvidence = False + + def set_cluster(self,cluster): + self.cluster = cluster + + def get_variables(self): + return self.node.get_cpd().variables + + def get_calculation_CDP(self): + return self.calCPD; + + def get_node(self): + return self.node + + def contains_node(self,node): + return self.node == node + +class FactorTree(object): + '''The factor tree contains for each node of the BayesNet a factor. It + is a directed graph with one root node. To speed up the reasoning it uses + a message based approach which stores calculated intermediate results at + edges. Thus, the first query is expensive and all following are easy calculated. + The speed of the first message calculation depends on how the tree was build. + Literature: Modeling and Reasoning with Bayesian Networks - Adnan Darwiche + Chapter 7 + ''' + + + def __init__(self,graph,rootNode): + self.graph = graph + self.rootNode = rootNode + + def calculate_PoE(self): + '''Calculates the probability of evidence with the set evidence''' + if not self.graph.graph['messagesValid']: + self.calculate_messages() + + cpd = self.calculate_marginal_forOne(self.rootNode) + + for v in cpd.get_variables()[:]: + cpd = cpd.marginalization(v) + + return cpd + + def calculate_marginal(self,variables): + ''' If evidence is set, then this methods calculates the posterior marginal. + With an empty evidence this is automatically the prior marginal.''' + if not self.graph.graph['messagesValid']: + self.calculate_messages() + + + resPT = primo.densities.ProbabilityTable.get_neutral_multiplication_PT() + + + for f in self.graph.nodes(): + if f.get_node() in variables: + resPT = resPT.multiplication(self.calculate_marginal_forOne(f)) + + resPT = resPT.normalize_as_jpt() + + return resPT + + def calculate_marginal_forOne(self,factor): + curCPD = factor.get_calculation_CDP().copy() + + for p in self.graph.predecessors(factor): + tmpCPD = self.graph[p][factor]['msgRightWay'] + curCPD = curCPD.multiplication(tmpCPD) + + for p in self.graph.neighbors(factor): + tmpCPD = self.graph[factor][p]['msgAgainstWay'] + curCPD = curCPD.multiplication(tmpCPD) + + for v in curCPD.get_variables()[:]: + if v != factor.get_node(): + curCPD = curCPD.marginalization(v) + + return curCPD + + + def draw(self): + '''Draws the FactorTree''' + import matplotlib.pyplot as plt + nx.draw_circular(self.graph) + plt.show() + + def calculate_messages(self): + ''' Calculates the messages and stores the intermediate results.''' + self.pull_phase(self.rootNode,self.graph) + self.push_phase(self.rootNode,self.graph,primo.densities.ProbabilityTable.get_neutral_multiplication_PT()) + self.graph.graph['messagesValid'] = True + + + def set_evidences(self,evidences): + self.graph.graph['messagesValid'] = False + + evNodes = zip(*evidences) + + for factor in self.graph.nodes(): + if factor.get_node() in evNodes[0]: + idx = evNodes[0].index(factor.get_node()) + factor.set_evidence(evidences[idx]) + + + + + def pull_phase(self,factor,graph): + + calCPD = factor.get_calculation_CDP() + #calculate the messages of the children + for child in graph.neighbors(factor): + tmpInput = self.pull_phase(child,graph) + + + #project each factor on the specific separator + separator = graph[factor][child]['separator'] + for var in tmpInput.variables[:]: + if var not in separator: + tmpInput = tmpInput.marginalization(var) + + + #save message on edge: it's the opposite of the direction of the edge + graph[factor][child]['msgAgainstWay'] = tmpInput + #calculate the new message + calCPD = calCPD.multiplication(tmpInput) + + return calCPD + + def push_phase(self,factor,graph,inCPD): + + for child in graph.neighbors(factor): + tmpCPD = inCPD.multiplication(factor.get_calculation_CDP()) + for child2 in graph.neighbors(factor): + if (child != child2): + tmpCPD = tmpCPD.multiplication(graph[factor][child2]['msgAgainstWay']) + + separator = graph[factor][child]['separator'] + #project on outgoing edge separator + for var in tmpCPD.variables: + if var not in separator: + tmpCPD = tmpCPD.marginalization(var) + + #add setOut to outgoing vars from child + #Message with the direction of the edge + graph[factor][child]['msgRightWay'] = tmpCPD + + + self.push_phase(child,graph,tmpCPD) + + +class FactorTreeFactory(object): + '''The FactorTreeFactory creates the FactorTree out of a BayesNet.''' + + def create_random_factortree(self,bayesNet): + ''' Creates a randomly structured FactorTree. This method is useful for testing + if reasoning works for arbitrary trees.''' + allNodes = bayesNet.get_all_nodes() + + if len(allNodes) == 0: + raise Exception("createRandomFactorTree: No nodes in given BayesNet") + + tn = allNodes.pop() + rootFactor = Factor(tn) + + graph = nx.DiGraph(messagesValid=False) + graph.add_node(rootFactor) + + usedNodes = [rootFactor] + + for n in allNodes[:]: + parentNode = choice(usedNodes[:]) + newFactor = Factor(n) + graph.add_edge(parentNode,newFactor, inVars=set(),outVars=set()) + usedNodes.append(newFactor) + + self.calculate_seperators_pull(rootFactor,graph) + self.calculate_seperators_push(rootFactor,graph,set()) + self.intersect_seperators(graph) + + self.calculate_clusters(rootFactor,graph,set()) + + + return FactorTree(graph,rootFactor) + + def create_greedy_factortree(self,bayesNet): + '''This method creates a factor the after the following algorithm: + + 1. Sort factors after containing variables (descending). + 2. For each node in the sorted list insert at it's best position. + + The best position is the node with the most joint variables.''' + + allNodes = bayesNet.get_all_nodes() + + if len(allNodes) == 0: + raise Exception("createRandomFactorTree: No nodes in given BayesNet") + + sortNodeList = [] + + for n in allNodes: + sortNodeList.append((len(n.get_cpd().get_variables()),n)) + + #sort node list + sortNodeList = sorted(sortNodeList,key=itemgetter(0),reverse=True) + + sortNodeList = zip(*sortNodeList) + sortNodeList = list(sortNodeList[1]) + + #root node with the most variables + rootFactor = Factor(sortNodeList.pop(0)) + + #create new graph for factor tree + graph = nx.DiGraph(messagesValid=False) + graph.add_node(rootFactor) + + #All nodes are added + for nd in sortNodeList[:]: + (ct,insFactor) = self.find_best_node_for_insertion(graph,rootFactor,set(nd.get_cpd().get_variables())) + nFactor = Factor(nd) + graph.add_edge(insFactor,nFactor, inVars=set(),outVars=set()) + + #For the later calculation the seperators are needed + self.calculate_seperators_pull(rootFactor,graph) + self.calculate_seperators_push(rootFactor,graph,set()) + self.intersect_seperators(graph) + + #the cluster are not necessarily needed but indicate how good the calculation of messages performs + self.calculate_clusters(rootFactor,graph,set()) + + + return FactorTree(graph,rootFactor) + + + def find_best_node_for_insertion(self,graph,factor,nodeSet): + '''finds the node in the graph with the most common variables to the given node''' + + curJointCount = len(set(factor.get_variables()) & nodeSet) + curInsertFactor = factor + + for nbs in graph.neighbors(factor): + (count,retFactor) = self.find_best_node_for_insertion(graph,nbs,nodeSet) + if count >= curJointCount: + curJointCount = count + curInsertFactor = retFactor + + return (curJointCount,curInsertFactor) + + + def calculate_seperators_pull(self,factor,graph): + + s = set() + pullSet = set(factor.get_variables()) + + #find all variables in outgoing edges for factor + for child in graph.neighbors(factor): + s = self.calculate_seperators_pull(child,graph) + graph[factor][child]['inVars'] = s + + pullSet = s | pullSet + + return pullSet + + def calculate_seperators_push(self,factor,graph,setOut): + + #add local vars to set + setOut = set(factor.get_variables()) | setOut + + + for child in graph.neighbors(factor): + tmpSet = copy.copy(setOut) + for child2 in graph.neighbors(factor): + if (child != child2): + tmpSet = tmpSet | graph[factor][child2]['inVars'] + + #add setOut to outgoing variables from the child + tmp = graph[factor][child]['outVars'] + graph[factor][child]['outVars'] = tmp | tmpSet + + + self.calculate_seperators_push(child,graph,tmpSet) + + + def intersect_seperators(self,graph): + + for n,nbrs in graph.adjacency_iter(): + for nbr,eattr in nbrs.items(): + eattr['separator'] = eattr['inVars'] & eattr['outVars'] + + def calculate_clusters(self,factor,graph,parent_seperator): + + localCluster = parent_seperator | set(factor.get_variables()) + + for n in graph.neighbors(factor): + tmpSeparator = graph[factor][n]['separator'] + localCluster = localCluster | tmpSeparator + self.calculate_clusters(n,graph,tmpSeparator) + + factor.set_cluster(localCluster) + + +class EasiestFactorElimination(object): + '''This is the easiest way for factor elimination.It's has the worst runtime because: + 1. Needed evidences are set (optional). + 2. All nodes are multiplied. + 3. The redundant variables are summed out + + Literature: Modeling and Reasoning with Bayesian Networks - Adnan Darwiche + Chapter 6-7 + ''' + + + + def __init__(self,bayesNet): + self.bn= bayesNet + + + def calculate_PriorMarginal(self,variables): + '''Calculates the prior marignal for the given variables. The resulting + CPD is returned.''' + nodes = self.bn.get_all_nodes() + + finCpd = nodes.pop().get_cpd() + + for n in nodes: + finCpd = finCpd.multiplication(n.get_cpd()) + + for v in finCpd.get_variables(): + if v not in variables: + finCpd = finCpd.marginalization(v) + + return finCpd + + def calculate_PosteriorMarginal(self,variables,evidence): + '''Calculates the posterior marginal for given variables and evidence. + It returns the resulting cpd.''' + nodes = self.bn.get_all_nodes() + + #List of evidences + ev_list = zip(*evidence) + # Special Case: First Node + node1 = nodes.pop() + if node1 in ev_list[0]: + ind = ev_list[0].index(node1) + finCpd = node1.get_cpd().set_evidence(evidence[ind]) + + else: + finCpd = node1.get_cpd() + + + # For all other nodes + for n in nodes: + if n in ev_list[0]: + #Set evidence and multiply + ind = ev_list[0].index(n) + nCPD = n.get_cpd().set_evidence(evidence[ind]) + finCpd = finCpd.multiplication(nCPD) + else: + #only multiply + finCpd = finCpd.multiplication(n.get_cpd()) + + + for v in finCpd.get_variables(): + if v not in variables: + finCpd = finCpd.marginalization(v) + + finCpd = finCpd.normalize_as_jpt() + + + return finCpd + + def calculate_PoE(self,evidence): + ''' Calculates the probabilty of evidence for the given evidence and returns the result.''' + + nodes = self.bn.get_all_nodes() + + unzipped_list = zip(*evidence) + + node1 = nodes.pop() + if node1 in unzipped_list[0]: + ind = unzipped_list[0].index(node1) + finCpd = node1.get_cpd().set_evidence(evidence[ind]) + + else: + finCpd = node1.get_cpd() + + for n in nodes: + if n in unzipped_list[0]: + ind = unzipped_list[0].index(n) + nCPD = n.get_cpd().set_evidence(evidence[ind]) + finCpd = finCpd.multiplication(nCPD) + else: + finCpd = finCpd.multiplication(n.get_cpd()) + + for v in finCpd.get_variables(): + finCpd = finCpd.marginalization(v) + + return finCpd + + diff --git a/primo/reasoning/factorelemination/EasiestFactorElimination.py b/primo/reasoning/factorelemination/EasiestFactorElimination.py deleted file mode 100644 index 74b9f14fad2507654cc8844edd69de67d77602ca..0000000000000000000000000000000000000000 --- a/primo/reasoning/factorelemination/EasiestFactorElimination.py +++ /dev/null @@ -1,105 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- -from primo.core import BayesNet -from primo.reasoning import DiscreteNode -import numpy - -class EasiestFactorElimination(object): - '''This is the easiest way for factor elimination.It's has the worst runtime because: - 1. Needed evidences are set (optional). - 2. All nodes are multiplied. - 3. The redundant variables are summed out - - Literature: Modeling and Reasoning with Bayesian Networks - Adnan Darwiche - Chapter 6-7 - ''' - - - - def __init__(self,bayesNet): - self.bn= bayesNet - - - def calculate_PriorMarginal(self,variables): - '''Calculates the prior marignal for the given variables. The resulting - CPD is returned.''' - nodes = self.bn.get_all_nodes() - - finCpd = nodes.pop().get_cpd() - - for n in nodes: - finCpd = finCpd.multiplication(n.get_cpd()) - - for v in finCpd.get_variables(): - if v not in variables: - finCpd = finCpd.marginalization(v) - - return finCpd - - def calculate_PosteriorMarginal(self,variables,evidence): - '''Calculates the posterior marginal for given variables and evidence. - It returns the resulting cpd.''' - nodes = self.bn.get_all_nodes() - - #List of evidences - ev_list = zip(*evidence) - # Special Case: First Node - node1 = nodes.pop() - if node1 in ev_list[0]: - ind = ev_list[0].index(node1) - finCpd = node1.get_cpd().set_evidence(evidence[ind]) - - else: - finCpd = node1.get_cpd() - - - # For all other nodes - for n in nodes: - if n in ev_list[0]: - #Set evidence and multiply - ind = ev_list[0].index(n) - nCPD = n.get_cpd().set_evidence(evidence[ind]) - finCpd = finCpd.multiplication(nCPD) - else: - #only multiply - finCpd = finCpd.multiplication(n.get_cpd()) - - - for v in finCpd.get_variables(): - if v not in variables: - finCpd = finCpd.marginalization(v) - - finCpd = finCpd.normalize_as_jpt() - - - return finCpd - - - - def calculate_PoE(self,evidence): - ''' Calculates the probabilty of evidence for the given evidence and returns the result.''' - - nodes = self.bn.get_all_nodes() - - unzipped_list = zip(*evidence) - - node1 = nodes.pop() - if node1 in unzipped_list[0]: - ind = unzipped_list[0].index(node1) - finCpd = node1.get_cpd().set_evidence(evidence[ind]) - - else: - finCpd = node1.get_cpd() - - for n in nodes: - if n in unzipped_list[0]: - ind = unzipped_list[0].index(n) - nCPD = n.get_cpd().set_evidence(evidence[ind]) - finCpd = finCpd.multiplication(nCPD) - else: - finCpd = finCpd.multiplication(n.get_cpd()) - - for v in finCpd.get_variables(): - finCpd = finCpd.marginalization(v) - - return finCpd diff --git a/primo/reasoning/factorelemination/Factor.py b/primo/reasoning/factorelemination/Factor.py deleted file mode 100644 index 2f7c6adf42aa8ba7ef36de43ca23acf5811305cb..0000000000000000000000000000000000000000 --- a/primo/reasoning/factorelemination/Factor.py +++ /dev/null @@ -1,36 +0,0 @@ -class Factor(object): - - def __init__(self,node): - self.node = node - self.calCPD = node.get_cpd().copy() - self.cluster = set() - self.isEvidence = False - - def __str__(self): - return self.node.name - - def set_evidence(self,evd): - self.calCPD = self.node.get_cpd().copy() - self.calCPD = self.calCPD.set_evidence(evd) - self.isEvidene = True - - def clear_evidence(self): - self.calCPD = self.node.get_cpd().copy() - self.isEvidence = False - - def set_cluster(self,cluster): - self.cluster = cluster - - def get_variables(self): - return self.node.get_cpd().variables - - def get_calculation_CDP(self): - return self.calCPD; - - def get_node(self): - return self.node - - def contains_node(self,node): - return self.node == node - - diff --git a/primo/reasoning/factorelemination/FactorTree.py b/primo/reasoning/factorelemination/FactorTree.py deleted file mode 100644 index 892da1419758da4215df2039e61c3d79171bdec2..0000000000000000000000000000000000000000 --- a/primo/reasoning/factorelemination/FactorTree.py +++ /dev/null @@ -1,141 +0,0 @@ - -import networkx as nx -import primo.reasoning.density.ProbabilityTable as ProbabilityTable - - -class FactorTree(object): - '''The factor tree contains for each node of the BayesNet a factor. It - is a directed graph with one root node. To speed up the reasoning it uses - a message based approach which stores calculated intermediate results at - edges. Thus, the first query is expensive and all following are easy calculated. - The speed of the first message calculation depends on how the tree was build. - Literature: Modeling and Reasoning with Bayesian Networks - Adnan Darwiche - Chapter 7 - ''' - - - def __init__(self,graph,rootNode): - self.graph = graph - self.rootNode = rootNode - - def calculate_PoE(self): - '''Calculates the probability of evidence with the set evidence''' - if not self.graph.graph['messagesValid']: - self.calculate_messages() - - cpd = self.calculate_marginal_forOne(self.rootNode) - - for v in cpd.get_variables()[:]: - cpd = cpd.marginalization(v) - - return cpd - - def calculate_marginal(self,variables): - ''' If evidence is set, then this methods calculates the posterior marginal. - With an empty evidence this is automatically the prior marginal.''' - if not self.graph.graph['messagesValid']: - self.calculate_messages() - - - resPT = ProbabilityTable.get_neutral_multiplication_PT() - - - for f in self.graph.nodes(): - if f.get_node() in variables: - resPT = resPT.multiplication(self.calculate_marginal_forOne(f)) - - resPT = resPT.normalize_as_jpt() - - return resPT - - def calculate_marginal_forOne(self,factor): - curCPD = factor.get_calculation_CDP().copy() - - for p in self.graph.predecessors(factor): - tmpCPD = self.graph[p][factor]['msgRightWay'] - curCPD = curCPD.multiplication(tmpCPD) - - for p in self.graph.neighbors(factor): - tmpCPD = self.graph[factor][p]['msgAgainstWay'] - curCPD = curCPD.multiplication(tmpCPD) - - for v in curCPD.get_variables()[:]: - if v != factor.get_node(): - curCPD = curCPD.marginalization(v) - - return curCPD - - - def draw(self): - '''Draws the FactorTree''' - import matplotlib.pyplot as plt - nx.draw_circular(self.graph) - plt.show() - - def calculate_messages(self): - ''' Calculates the messages and stores the intermediate results.''' - self.pull_phase(self.rootNode,self.graph) - self.push_phase(self.rootNode,self.graph,ProbabilityTable.get_neutral_multiplication_PT()) - self.graph.graph['messagesValid'] = True - - - def set_evidences(self,evidences): - self.graph.graph['messagesValid'] = False - - evNodes = zip(*evidences) - - for factor in self.graph.nodes(): - if factor.get_node() in evNodes[0]: - idx = evNodes[0].index(factor.get_node()) - factor.set_evidence(evidences[idx]) - - - - - def pull_phase(self,factor,graph): - - calCPD = factor.get_calculation_CDP() - #calculate the messages of the children - for child in graph.neighbors(factor): - tmpInput = self.pull_phase(child,graph) - - - #project each factor on the specific separator - separator = graph[factor][child]['separator'] - for var in tmpInput.variables[:]: - if var not in separator: - tmpInput = tmpInput.marginalization(var) - - - #save message on edge: it's the opposite of the direction of the edge - graph[factor][child]['msgAgainstWay'] = tmpInput - #calculate the new message - calCPD = calCPD.multiplication(tmpInput) - - return calCPD - - def push_phase(self,factor,graph,inCPD): - - for child in graph.neighbors(factor): - tmpCPD = inCPD.multiplication(factor.get_calculation_CDP()) - for child2 in graph.neighbors(factor): - if (child != child2): - tmpCPD = tmpCPD.multiplication(graph[factor][child2]['msgAgainstWay']) - - separator = graph[factor][child]['separator'] - #project on outgoing edge separator - for var in tmpCPD.variables: - if var not in separator: - tmpCPD = tmpCPD.marginalization(var) - - #add setOut to outgoing vars from child - #Message with the direction of the edge - graph[factor][child]['msgRightWay'] = tmpCPD - - - self.push_phase(child,graph,tmpCPD) - - - - - diff --git a/primo/reasoning/factorelemination/FactorTreeFactory.py b/primo/reasoning/factorelemination/FactorTreeFactory.py deleted file mode 100644 index 2302c4b896136c0194c193c06f045b01db61c5de..0000000000000000000000000000000000000000 --- a/primo/reasoning/factorelemination/FactorTreeFactory.py +++ /dev/null @@ -1,166 +0,0 @@ - -import networkx as nx -import copy -from primo.reasoning.factorelemination import Factor -from primo.reasoning.factorelemination import FactorTree -from random import choice -from operator import itemgetter - -class FactorTreeFactory(object): - '''The FactorTreeFactory creates the FactorTree out of a BayesNet.''' - - def create_random_factortree(self,bayesNet): - ''' Creates a randomly structured FactorTree. This method is useful for testing - if reasoning works for arbitrary trees.''' - allNodes = bayesNet.get_all_nodes() - - if len(allNodes) == 0: - raise Exception("createRandomFactorTree: No nodes in given BayesNet") - - tn = allNodes.pop() - rootFactor = Factor(tn) - - graph = nx.DiGraph(messagesValid=False) - graph.add_node(rootFactor) - - usedNodes = [rootFactor] - - for n in allNodes[:]: - parentNode = choice(usedNodes[:]) - newFactor = Factor(n) - graph.add_edge(parentNode,newFactor, inVars=set(),outVars=set()) - usedNodes.append(newFactor) - - self.calculate_seperators_pull(rootFactor,graph) - self.calculate_seperators_push(rootFactor,graph,set()) - self.intersect_seperators(graph) - - self.calculate_clusters(rootFactor,graph,set()) - - - return FactorTree(graph,rootFactor) - - def create_greedy_factortree(self,bayesNet): - '''This method creates a factor the after the following algorithm: - - 1. Sort factors after containing variables (descending). - 2. For each node in the sorted list insert at it's best position. - - The best position is the node with the most joint variables.''' - - allNodes = bayesNet.get_all_nodes() - - if len(allNodes) == 0: - raise Exception("createRandomFactorTree: No nodes in given BayesNet") - - sortNodeList = [] - - for n in allNodes: - sortNodeList.append((len(n.get_cpd().get_variables()),n)) - - #sort node list - sortNodeList = sorted(sortNodeList,key=itemgetter(0),reverse=True) - - sortNodeList = zip(*sortNodeList) - sortNodeList = list(sortNodeList[1]) - - #root node with the most variables - rootFactor = Factor(sortNodeList.pop(0)) - - #create new graph for factor tree - graph = nx.DiGraph(messagesValid=False) - graph.add_node(rootFactor) - - #All nodes are added - for nd in sortNodeList[:]: - (ct,insFactor) = self.find_best_node_for_insertion(graph,rootFactor,set(nd.get_cpd().get_variables())) - nFactor = Factor(nd) - graph.add_edge(insFactor,nFactor, inVars=set(),outVars=set()) - - #For the later calculation the seperators are needed - self.calculate_seperators_pull(rootFactor,graph) - self.calculate_seperators_push(rootFactor,graph,set()) - self.intersect_seperators(graph) - - #the cluster are not necessarily needed but indicate how good the calculation of messages performs - self.calculate_clusters(rootFactor,graph,set()) - - - return FactorTree(graph,rootFactor) - - - def find_best_node_for_insertion(self,graph,factor,nodeSet): - '''finds the node in the graph with the most common variables to the given node''' - - curJointCount = len(set(factor.get_variables()) & nodeSet) - curInsertFactor = factor - - for nbs in graph.neighbors(factor): - (count,retFactor) = self.find_best_node_for_insertion(graph,nbs,nodeSet) - if count >= curJointCount: - curJointCount = count - curInsertFactor = retFactor - - return (curJointCount,curInsertFactor) - - - def calculate_seperators_pull(self,factor,graph): - - s = set() - pullSet = set(factor.get_variables()) - - #find all variables in outgoing edges for factor - for child in graph.neighbors(factor): - s = self.calculate_seperators_pull(child,graph) - graph[factor][child]['inVars'] = s - - pullSet = s | pullSet - - return pullSet - - def calculate_seperators_push(self,factor,graph,setOut): - - #add local vars to set - setOut = set(factor.get_variables()) | setOut - - - for child in graph.neighbors(factor): - tmpSet = copy.copy(setOut) - for child2 in graph.neighbors(factor): - if (child != child2): - tmpSet = tmpSet | graph[factor][child2]['inVars'] - - #add setOut to outgoing variables from the child - tmp = graph[factor][child]['outVars'] - graph[factor][child]['outVars'] = tmp | tmpSet - - - self.calculate_seperators_push(child,graph,tmpSet) - - - def intersect_seperators(self,graph): - - for n,nbrs in graph.adjacency_iter(): - for nbr,eattr in nbrs.items(): - eattr['separator'] = eattr['inVars'] & eattr['outVars'] - - def calculate_clusters(self,factor,graph,parent_seperator): - - localCluster = parent_seperator | set(factor.get_variables()) - - for n in graph.neighbors(factor): - tmpSeparator = graph[factor][n]['separator'] - localCluster = localCluster | tmpSeparator - self.calculate_clusters(n,graph,tmpSeparator) - - factor.set_cluster(localCluster) - - - - - - - - - - diff --git a/primo/reasoning/factorelemination/__init__.py b/primo/reasoning/factorelemination/__init__.py deleted file mode 100644 index f0b37c0106de2fb116f0a2db914dddba58b7a47b..0000000000000000000000000000000000000000 --- a/primo/reasoning/factorelemination/__init__.py +++ /dev/null @@ -1,4 +0,0 @@ -from EasiestFactorElimination import EasiestFactorElimination -from Factor import Factor -from FactorTree import FactorTree -from FactorTreeFactory import FactorTreeFactory \ No newline at end of file diff --git a/primo/reasoning/particlebased/__init__.py b/primo/reasoning/particlebased/__init__.py deleted file mode 100644 index 866e708dafa611f17b59cb6c98ffefb5e75dbc9e..0000000000000000000000000000000000000000 --- a/primo/reasoning/particlebased/__init__.py +++ /dev/null @@ -1,2 +0,0 @@ -# -*- coding: utf-8 -*- -import ParticleFilterDBN \ No newline at end of file diff --git a/primo/reasoning/particlebased/ParticleFilterDBN.py b/primo/reasoning/particlefilter.py similarity index 98% rename from primo/reasoning/particlebased/ParticleFilterDBN.py rename to primo/reasoning/particlefilter.py index 52fd87b64f434f6f438dacdf5db046b096a49725..e34e6b7143d60599ebdc389e662212d9799dddc9 100644 --- a/primo/reasoning/particlebased/ParticleFilterDBN.py +++ b/primo/reasoning/particlefilter.py @@ -1,10 +1,12 @@ # -*- coding: utf-8 -*- -from primo.core import BayesNet -from primo.core import DynamicBayesNet -import random + import copy +import random import time +from primo.networks import BayesNet +from primo.networks import DynamicBayesNet + class Particle(object): ''' This is the basic particle class used by the DBN particle filter.