def add_new_state_node(self, t): if len(self.variables["states"]) > t: # the state already exists in the graph. return else: if t == 0: P = self.get_prior_factor() self.factors["transition"].append(pm.DiscreteDistribution(P)) # add prior factor self.variables["states"].append(pm.State( self.factors["transition"][t], name="Damage {}".format(t))) self.add_node(self.variables["states"][t]) else: T = self.get_transition_factor() # check if we have a controlA if len(self.variables["controlAs"]) > t-1: self.factors["transition"].append(pm.ConditionalProbabilityTable(T, [self.factors["transition"][t-1],self.factors["controlA"][t-1]])) else: self.factors["transition"].append(pm.ConditionalProbabilityTable(T, [self.factors["transition"][t-1],self.factors["controlP"][t-1]])) # add RV as a node in the graph self.variables["states"].append(pm.State( self.factors["transition"][t], name="Damage {}".format(t))) self.add_node(self.variables["states"][t]) # connect node via transition edge self.add_edge(self.variables["states"][t-1], self.variables["states"][t] ) # connect node via control edge if len(self.variables["controlAs"]) > t-1: self.add_edge(self.variables["controlAs"][t-1], self.variables["states"][t] ) else: self.add_edge(self.variables["controlPs"][t-1], self.variables["states"][t] )
def __set_target_probability(self): for i in range(action.Action.num_actions): if i != action.Action.ST: prob_value = 0.5 if self.__target_vec != None: direction_vec = action.VECTOR[i] direction_vec.normalize() self.__target_vec.normalize() dot = self.__target_vec.dot_product(direction_vec) self.__target_dots[i] = dot prob_value = self.__target_threshold + ( 1.0 - self.__target_threshold) * max(0, dot) self.__d_target[i] = pm.ConditionalProbabilityTable( [['T', 'T', prob_value], ['T', 'F', 1.0 - prob_value], ['F', 'T', 0.5], ['F', 'F', 0.5]], [self.__d_direction[i]]) else: self.__target_dots[i] = 0 self.__d_target[i] = pm.ConditionalProbabilityTable( [['T', 'T', 0.5], ['T', 'F', 0.5], ['F', 'T', 0.5], ['F', 'F', 0.5]], [self.__d_direction[i]]) self.__s_target = [ pm.State(self.__d_target[i], name='target_' + str(i)) for i in range(action.Action.num_actions) ]
def fit(self, similarBldDF): ''' climate zone, Design cooling load and principle building activity are parents attributes of main cooling equipment. Census division, main cooling equipment, cooling degree days, percentage of building cooled are four parent attribute of high efficient building Attributes: similarBldDF, a pandas DataFrame object includes a group of building similar to the proposed building.This object is used to train the Bayesian Network classifier. ''' climateZoneDict,COOLLOADDict,principleActivityNDict,MAINCLCPTList,MAINCLDict,CDD65NDict,HECSCPTList=self.attributeDistribution(similarBldDF) climateZone=pm.DiscreteDistribution(climateZoneDict) designCoolingLoad=pm.DiscreteDistribution(COOLLOADDict) principleBuildingActivity=pm.DiscreteDistribution(principleActivityNDict) coolingDegreeDays = pm.DiscreteDistribution(CDD65NDict) #MCE_CPT is the conditional probability table of main cooling equipment mainCoolingEquipmentCPT=pm.ConditionalProbabilityTable(MAINCLCPTList,[climateZone,designCoolingLoad,principleBuildingActivity]) #HECS_CPT is the conditional probability table of high efficient cooling system. highEfficientCoolingSystemCPT=pm.ConditionalProbabilityTable(HECSCPTList,[mainCoolingEquipmentCPT,principleBuildingActivity,\ coolingDegreeDays]) #the first layer parent attributes p1_climateZone=pm.Node(climateZone,name="climateZone")#climateZone p1_COOLLOAD=pm.Node(designCoolingLoad,name="COOLLOAD")#COOLLOAD p1_principleActivity=pm.Node(principleBuildingActivity,name="principleActivity")#principleActivity #the second layer parent attributes #the main cooling equipment p2_MAINCL = pm.Node(mainCoolingEquipmentCPT,name="MAINCL")# p2_CDD65 = pm.Node(coolingDegreeDays,name="CDD65") #high efficient cooling system p_HECS = pm.Node(highEfficientCoolingSystemCPT, name="highEfficientCoolingSystemCPT") #the Bayesian Network for the main cooling equipment modelMCE = pm.BayesianNetwork("Main cooling equipment") modelMCE.add_nodes(p1_climateZone,p1_COOLLOAD,p1_principleActivity,p2_MAINCL,p2_CDD65,p_HECS) modelMCE.add_edge(p1_climateZone,p2_MAINCL) modelMCE.add_edge(p1_COOLLOAD,p2_MAINCL) modelMCE.add_edge(p1_principleActivity,p2_MAINCL) modelMCE.add_edge(p2_MAINCL,p_HECS) modelMCE.add_edge(p1_principleActivity,p_HECS) modelMCE.add_edge(p2_CDD65,p_HECS) modelMCE.bake() self.BN4CLfitted=modelMCE
def add_new_obs_node(self, t, sensor_measurement): O = self.get_observation_factor(sensor_measurement) self.factors["observation"].append(pm.ConditionalProbabilityTable(O, [self.factors["transition"][t]])) self.variables["observations"].append(pm.State( self.factors["observation"][t], name="Observation {}".format(t))) self.evidence["Observation {}".format(t)] = json.dumps(sensor_measurement) # add RV as a node in the graph self.add_node(self.variables["observations"][t]) # connect node via observation edge self.add_edge(self.variables["states"][t], self.variables["observations"][t] )
def add_new_ref_obs_node(self, t): if len(self.variables["ref_observations"]) > t: #node is already in the graph return else: self.factors["ref_observation"].append(pm.ConditionalProbabilityTable(self.Q_factor, [self.factors["transition"][t]])) self.variables["ref_observations"].append(pm.State(self.factors["ref_observation"][t], name="Ref. Observation {}".format(t))) # add RV as a node in the graph self.add_node(self.variables["ref_observations"][t]) # connect node via ref observation edge self.add_edge(self.variables["states"][t], self.variables["ref_observations"][t] )
def add_new_controlP_node(self,t): # 'Predicted' or estimated control node if len(self.variables["controlPs"]) > t: #node is already in the graph return else: C = self.get_controlP_factor() self.factors["controlP"].append(pm.ConditionalProbabilityTable(C, [self.factors["transition"][t]])) self.variables["controlPs"].append(pm.State(self.factors["controlP"][t], name="ControlP {}".format(t))) # add RV as a node in the graph self.add_node(self.variables["controlPs"][t]) # connect node via observation edge self.add_edge(self.variables["states"][t], self.variables["controlPs"][t] )
def probDiagnose(percList): """ Funzione che riceve in input una lista con tre tuple contenenti ognuna una categoria e la loro percentuale e, attraverso l'uso di una rete bayesiana ed il calcolo delle probabilità condizionate, restituisce una lista con le probabilità delle categorie delle diagnosi condizionate dalle categorie dei sintomi Parameters ---------- percList: list Lista contenente tre tuple: ogni tupla contiene una categoria e la rispettiva percentuale(per i sintomi) Returns ------- condProbList: list Lista contenente tre tuple: ogni tupla contiene una categoria e la rispettiva probabilità(per le diagnosi) """ import pomegranate as pg sym = pg.DiscreteDistribution({ 'gen': 192. / 389, 'sup': 125. / 389, 'inf': 72. / 389 }) diagn = pg.ConditionalProbabilityTable( [['gen', 'gen', 0.5], ['gen', 'sup', 0.25], ['gen', 'inf', 0.25], ['sup', 'gen', 0.20], ['sup', 'sup', 0.75], ['sup', 'inf', 0.05], ['inf', 'gen', 0.2], ['inf', 'sup', 0.05], ['inf', 'inf', 0.75]], [sym]) s1 = pg.State(sym, name="sym") s2 = pg.State(diagn, name="diagn") model = pg.BayesianNetwork("Diagnose finder") model.add_states(s1, s2) model.add_edge(s1, s2) model.bake() condProbList = [] for i in percList: beliefs1 = model.predict_proba({'sym': i[1]}) condProbList.append(beliefs1[1].parameters[0]) return condProbList
def _solve_bayes_network(cpts, conditionals=None): print(f'cpts: {cpts}') print(f'conditionals: {cpts}') model = pmg.BayesianNetwork("User Produced Model") states = [] distributions = [] cond = [] _cond_stage = [] def _translator(string): if string == 0 or string == '0': return 'True' elif string == 1 or string == '1': return 'False' else: return None counter = 0 for i, name in enumerate(cpts.keys()): temp_dict = cpts[name].to_dict() if name not in conditionals: for k in temp_dict.keys(): distributions.append(pmg.DiscreteDistribution(temp_dict[k])) states.append(pmg.State(distributions[counter], name=name)) counter += 1 else: _cond_stage.append(i) for col in temp_dict.keys(): for val in temp_dict[col].keys(): arr = [_translator(col), val, temp_dict[col][val]] cond.append(arr) print(f'cond: {cond}') states.append( pmg.State(pmg.ConditionalProbabilityTable(cond, distributions), name=name)) for i, s in enumerate(states): print(f'i: {i}') print(f's: {s}') model.add_states(s) if i not in _cond_stage and _cond_stage: model.add_edge(s, states[_cond_stage[0]]) model.bake() return model
def _calculate_one(self) -> np.ndarray: """Run the calculation """ # Get the sequence of states dist_1 = pm.DiscreteDistribution( {"wet": 0.5, "dry": 0.5} ) # random starting point dist_2 = pm.ConditionalProbabilityTable( [ ["wet", "wet", self.param["pi_1"]], ["wet", "dry", 1 - self.param["pi_1"]], ["dry", "wet", 1 - self.param["pi_2"]], ["dry", "dry", self.param["pi_2"]], ], [dist_1], ) markov_chain = pm.MarkovChain([dist_1, dist_2]) years = self._get_time("all") states = markov_chain.sample(years.size) # Get the conditional expected value mu_1_vec = self.param["mu_1"] + self.param["gamma_1"] * years mu_2_vec = self.param["mu_2"] + self.param["gamma_2"] * years mu_vec = mu_1_vec mu_vec[np.where(np.array(states) == "wet")] = mu_2_vec[ np.where(np.array(states) == "wet") ] # get conditional variance sigma_vec = self.param["coeff_var"] * mu_vec sigma_vec[sigma_vec < self.param["sigma_min"]] = self.param["sigma_min"] # get and the streamflow sflow = np.exp(np.random.normal(loc=mu_vec, scale=sigma_vec)) return sflow
def export_pom(net, by='index'): ''' Returns ------- pomegranate BN Model based on given DAG. Assume my "sort" function correctly returns a list where children are allways ranked higher than parents. If Pommegranate is used to estimate model likelihood, all outcomes must be of the same data type. Either All int or all string. ''' s = topoSort(net.export_nds()) model = pm.BayesianNetwork("DIY_GRN") # Convert Top Level nodes to Discrete distributions top = [i for i in s if len(i.par) == 0] topStates = {} for n in top: pr = n.cpt['Prob'].to_dict() if by == 'index': va = n.cpt[n.idx].to_dict() else: va = n.cpt[n.label].to_dict() dist = {} for v in va.keys(): dist[va[v]] = pr[v] dist = pm.DiscreteDistribution(dist) if by == 'index': state = pm.Node(dist, name=str(n.idx)) topStates[str(n.idx)] = state else: state = pm.Node(dist, name=str(n.label)) topStates[str(n.label)] = state model.add_state(state) # Convert Depent Nodes to Conditional Distributions dep = [i for i in s if len(i.par) != 0] depStates = {} for n in dep: # Convert floats cpt outcome levels to integers if needed if isinstance(n.cpt.iloc[0, 0], np.int64): cpt = [fl(l) for l in n.cpt.values.tolist()] else: cpt = n.cpt.values.tolist() # Vector of ID for each parent if by == 'index': par_id = [str(i.idx) for i in n.par] else: par_id = [str(i.label) for i in n.par] # Validate that all parents have been processed for p in par_id: if (not p in topStates.keys()) and (not p in depStates.keys()): print("Problem with parent:", p, "of node:", n.idx) return [topStates, depStates] par = [ topStates[i] if i in topStates.keys() else depStates[i] for i in par_id if i in topStates.keys() or i in depStates.keys() ] cpt = pm.ConditionalProbabilityTable(cpt, [p.distribution for p in par]) if by == 'index': state = pm.Node(cpt, name=str(n.idx)) depStates[str(n.idx)] = state else: state = pm.Node(cpt, name=str(n.label)) depStates[str(n.label)] = state # Add node to model model.add_state(state) # Add edges from parent to this node for p in par: model.add_edge(p, state) # Assemble and "Bake" model model.bake() return (model)
import matplotlib.image import itertools as it import pomegranate as pom import pygraphviz import tempfile F = 'Fraud' T = 'Travel' OD = 'OwnsDevice' FP = 'ForeignPurchase' OP = 'OnlinePurchase' travelDist = pom.DiscreteDistribution({False: 0.05, True: 0.95}) foreignPurchaseDist = pom.ConditionalProbabilityTable( [[False, False, 0.9999], [False, True, 0.0001], [True, False, 0.12], [True, True, 0.88]], [travelDist]) ownsDeviceDist = pom.DiscreteDistribution({False: 0.3, True: 0.7}) onlinePurchaseDist = pom.ConditionalProbabilityTable( [[False, False, 0.9995], [False, True, 0.0005], [True, False, 0.60], [True, True, 0.40]], [ownsDeviceDist]) fraudDist = pom.ConditionalProbabilityTable( [[False, False, False, 0.25], [False, True, False, 0.15], [True, False, False, 0.20], [True, True, False, 0.0005], [False, False, True, 0.75], [False, True, True, 0.85], [True, False, True, 0.80], [True, True, True, 0.9995]], [onlinePurchaseDist, travelDist])
def __init__(self, map_manager, agent_type, sampling=False): self.__map_manager = map_manager self.__position = None self.__percept = None self.__action_map = None self.__target_threshold = 0.3 self.__max_prob_dir = None # target dot products self.__target_dots = [None] * action.Action.num_actions # Random variables marked as true will be considered in the bayesian net self._considered = { 'target': True, 'danger': True, 'obstruction': True, 'visibility': True, 'hider': True, 'seeker': True, 'blockage': True } if agent_type == agent.AgentType.Seeker: self._considered['danger'] = False self.__sampling = sampling # Probability distributions self.__d_direction = [ pm.DiscreteDistribution({ 'T': 0.5, 'F': 0.5 }) for i in range(action.Action.num_actions) ] self.__s_direction = [ pm.State(self.__d_direction[i], name='direction_' + str(i)) for i in range(action.Action.num_actions) ] # Random vars, probability distributions and state vars of considered variables # in the bayesian net if self._considered['target']: self.__r_target = [None] * action.Action.num_actions self.__d_target = [None] * action.Action.num_actions self.__s_target = None if self._considered['danger']: self.__r_danger = [None] * action.Action.num_actions self.__d_danger = [ pm.ConditionalProbabilityTable( [['T', '0', 0.99], ['T', '1', 0.01], ['F', '0', 0.5], ['F', '1', 0.5]], [self.__d_direction[i]]) for i in range(action.Action.num_actions) ] self.__s_danger = [ pm.State(self.__d_danger[i], name='danger_' + str(i)) for i in range(action.Action.num_actions) ] if self._considered['obstruction']: self.__r_obstruction = [None] * action.Action.num_actions self.__d_obstruction = [ pm.ConditionalProbabilityTable( [['T', '0', 0.001], ['T', '1', 0.003], ['T', '2', 0.006], ['T', '3', 0.99], ['F', '0', 1. / 4], ['F', '1', 1. / 4], ['F', '2', 1. / 4], ['F', '3', 1. / 4]], [self.__d_direction[i]]) for i in range(action.Action.num_actions) ] self.__s_obstruction = [ pm.State(self.__d_obstruction[i], name='obstruction_' + str(i)) for i in range(action.Action.num_actions) ] if self._considered['visibility']: self.__r_visibility = [None] * action.Action.num_actions self.__d_visibility = [ pm.ConditionalProbabilityTable( [['T', '0', 0.001], ['T', '1', 0.003], ['T', '2', 0.006], ['T', '3', 0.99], ['F', '0', 1. / 4], ['F', '1', 1. / 4], ['F', '2', 1. / 4], ['F', '3', 1. / 4]], [self.__d_direction[i]]) for i in range(action.Action.num_actions) ] self.__s_visibility = [ pm.State(self.__d_visibility[i], name='visibility_' + str(i)) for i in range(action.Action.num_actions) ] cpt_a = [['T', '0', 0.9], ['T', '1', 0.066], ['T', '2', 0.033], ['F', '0', 1. / 3], ['F', '1', 1. / 3], ['F', '2', 1. / 3]] cpt_b = [['T', '0', 0.9], ['T', '1', 0.077], ['T', '2', 0.022], ['F', '0', 1. / 3], ['F', '1', 1. / 3], ['F', '2', 1. / 3]] target_cpt = None if self._considered['hider']: if agent_type == agent.AgentType.Hider: target_cpt = cpt_a elif agent_type == agent.AgentType.Seeker: target_cpt = cpt_b self.__r_hider = [None] * action.Action.num_actions self.__d_hider = [ pm.ConditionalProbabilityTable(target_cpt, [self.__d_direction[i]]) for i in range(action.Action.num_actions) ] self.__s_hider = [ pm.State(self.__d_hider[i], name='hider_' + str(i)) for i in range(action.Action.num_actions) ] if self._considered['seeker']: if agent_type == agent.AgentType.Hider: target_cpt = cpt_b elif agent_type == agent.AgentType.Seeker: target_cpt = cpt_a self.__r_seeker = [None] * action.Action.num_actions self.__d_seeker = [ pm.ConditionalProbabilityTable(target_cpt, [self.__d_direction[i]]) for i in range(action.Action.num_actions) ] self.__s_seeker = [ pm.State(self.__d_seeker[i], name='seeker_' + str(i)) for i in range(action.Action.num_actions) ] if self._considered['blockage']: self.__r_blockage = [None] * action.Action.num_actions self.__d_blockage = [ pm.ConditionalProbabilityTable( [['T', '0', 0.999999], ['T', '1', 0.000001], ['F', '0', 0.5], ['F', '1', 0.5]], [self.__d_direction[i]]) for i in range(action.Action.num_actions) ] self.__s_blockage = [ pm.State(self.__d_blockage[i], name='blockage_' + str(i)) for i in range(action.Action.num_actions) ] # State objects(for pomegranate) library which hold both the distribution as well as name self.__model = None self.__inferred_results = None self.__direction_probs = [None] * action.Action.num_actions self.__direction_dist = None
def get_conditional_probability_table(self, conditional_probability_table): tbl = self._rows.get_table() cpts = self._parents.get_parents_table(conditional_probability_table) if cpts is None: return None out = pg.ConditionalProbabilityTable(tbl, cpts) return out
# A -> B -> C n = 6 t = s - 3 distA = pm.DiscreteDistribution({ "F": max_cdf[n - 1, t], "S": 1 - max_cdf[n - 1, t] }) cpd = [ ["F", "F", max_cdf[n - 1, t]], ["F", "S", 1 - max_cdf[n - 1, t]], ["S", "F", max_cdf[n, t]], ["S", "S", 1 - max_cdf[n, t]], ] distB_A = pm.ConditionalProbabilityTable(cpd, [distA]) distC_B = pm.ConditionalProbabilityTable(cpd, [distB_A]) # distD_C = pm.ConditionalProbabilityTable(cpd, [distC_B]) A = pm.Node(distA, name="A") B = pm.Node(distB_A, name="B") C = pm.Node(distC_B, name="C") # D = pm.Node(distD_C, name="D") model = pm.BayesianNetwork("Chain Model") model.add_states(A, B, C) #, D) model.add_edge(A, B) model.add_edge(B, C) # model.add_edge(C, D) model.bake()
t = 4 ppd_A = { "S": sf[n, t - 2], "F": 1 - sf[n, t - 2], } dist_A = pm.DiscreteDistribution(ppd_A) cpd_B_A = [ ["S", "S", sf[n + 1, t - 1]], ["S", "F", 1 - sf[n + 1, t - 1]], ["F", "S", sf[n, t - 1]], ["F", "F", 1 - sf[n, t - 1]], ] dist_B_A = pm.ConditionalProbabilityTable(cpd_B_A, [dist_A]) cpd_C_B = [ ["S", "S", sf[n + 1, t]], ["S", "F", 1 - sf[n + 1, t]], ["F", "S", sf[n, t]], ["F", "F", 1 - sf[n, t]], ] dist_C_B = pm.ConditionalProbabilityTable(cpd_C_B, [dist_B_A]) A = pm.Node(dist_A, name="A") B = pm.Node(dist_B_A, name="B") C = pm.Node(dist_C_B, name="C") model = pm.BayesianNetwork("Chain Model")
def export_pom(self): ''' Returns ------- pomegranate BN Model based on given DAG. Assume my "sort" function correctly returns a list where children are allways ranked higher than parents ''' s = self.sort_nodes(l=list(self.nds.values())) model = pm.BayesianNetwork("DIY_GRN") # Convert Top Level nodes to Discrete distributions top = [i for i in s if len(i.par) == 0] topStates = {} for n in top: pr = n.cpt['Prob'].to_dict() va = n.cpt[n.idx].to_dict() dist = {} for v in va.keys(): dist[va[v]] = pr[v] dist = pm.DiscreteDistribution(dist) state = pm.Node(dist, name="G" + str(n.idx)) topStates["G" + str(n.idx)] = state model.add_state(state) # Convert Depent Nodes to Conditional Distributions dep = [i for i in s if len(i.par) != 0] depStates = {} for n in dep: # Convert floats cpt outcome levels to integers if needed if isinstance(n.cpt.iloc[0, 0], np.int64): cpt = [fl(l) for l in n.cpt.values.tolist()] else: cpt = n.cpt.values.tolist() # Vector of ID for each parent par_id = ["G" + str(i.idx) for i in n.par] # Validate that all parents have been processed for p in par_id: if (not p in topStates.keys()) and (not p in depStates.keys()): print("Problem with parent:", p, "of node:", n.idx) return [topStates, depStates] # Get all parents found in the topStates dict par = [topStates[i] for i in par_id if i in topStates.keys()] # Add all parents in the depStates dict par = par + [depStates[i] for i in par_id if i in depStates.keys()] cpt = pm.ConditionalProbabilityTable(cpt, [p.distribution for p in par]) state = pm.Node(cpt, name="G" + str(n.idx)) depStates["G" + str(n.idx)] = state # Add node to model model.add_state(state) # Add edges from parent to this node for p in par: model.add_edge(p, state) # Assemble and "Bake" model model.bake() return (topStates, depStates, model)
def create_graph(self, name_network="Byesian netowrk"): """ Metodo per create il modello di rete bayesiana (tramite pomegranate) a partire dai dati caricati dal file XML. :param name_network: nome della rete bayesiana :type name_network: stringa :return: Modello pomegranate della rete bayesiana :rtype: pomegranate.BayesianNetwork """ cpt_node = {} pg_node = {} model = pomegranate.BayesianNetwork(name_network) for node_name in self.nodes_list: if not self.parents[node_name]: dict_dist = {} for elem in self.name_classes_nodes[node_name]: dict_dist[elem] = self.cpt_nodes[node_name][0][self.name_classes_nodes[node_name].index(elem)] # print("name_prior: {}".format(node_name)) self.nodes[node_name] = pomegranate.DiscreteDistribution(dict_dist) # print(self.nodes[node_name]) pg_node[node_name] = pomegranate.Node(self.nodes[node_name], name=node_name) else: cpt_pg = [] parent_list = self.parents[node_name].copy() parent_list.reverse() mod_list = [len(self.name_classes_nodes[node_name])] for i in range(len(parent_list) - 1): mul = mod_list[i] * len(self.name_classes_nodes[parent_list[i]]) mod_list.append(mul) cont = 0 for elem in self.cpt_nodes[node_name]: for prob in elem: row = [prob, self.name_classes_nodes[node_name][cont % mod_list[0]]] for parent in parent_list: attr = (cont // mod_list[parent_list.index(parent)]) % len(self.name_classes_nodes[parent]) row.append(self.name_classes_nodes[parent][attr]) cont += 1 row.reverse() cpt_pg.append(row) cpt_node[node_name] = cpt_pg for node_name in self.nodes_list: if self.parents[node_name]: # print("parents: {}".format(self.parents[node_name])) parent_node_list = [] for parent in self.parents[node_name]: parent_node_list.append(self.nodes[parent]) self.nodes[node_name] = pomegranate.ConditionalProbabilityTable(cpt_node[node_name], parent_node_list) # print("node {}: {}".format(node_name,self.nodes[node_name])) # print("parent_node_list: {}".format(parent_node_list)) pg_node[node_name] = pomegranate.Node(self.nodes[node_name], name=node_name) for node_name in self.nodes_list: # le distribuzioni di output seguono l'ordine con cui vengono aggiunti i nodi qui model.add_node(pg_node[node_name]) for node_name in self.nodes_list: # print("name {}".format(node_name)) for parent in self.parents[node_name]: # print("parent {}".format(parent)) model.add_edge(pg_node[parent], pg_node[node_name]) model.bake() return model