def create_network(max_parents, max_children, connections):
    
    '''
    Takes in max parents / chilldren per node constraints and network definition (connections). 
    Outputs model and list of nodes included in network.
    '''
    
    model = BayesianModel([connections[0]])
    nodes_added = []
    
    for edge in connections[1:]:
            #whichever has more children 
            #Node A
            try:
                if edge[0] in nodes_added and edge[1] in nodes_added: #both already in network
                    if len(model.get_children(edge[0])) < max_children and len(model.get_children(edge[1])) < max_children:
                        if len(model.get_parents(edge[0])) < max_parents and len(model.get_parents(edge[1])) < max_parents:
                            add_first = random.choice([0,1])
                            model.add_edge(edge[add_first],edge[1-add_first])

                        if len(model.get_parents(edge[0])) < max_parents and len(model.get_parents(edge[1])) >= max_parents:
                            model.add_edge(edge[1],edge[0])

                        if len(model.get_parents(edge[0])) >= max_parents and len(model.get_parents(edge[1])) < max_parents:
                            model.add_edge(edge[0],edge[1])

                    if len(model.get_children(edge[0])) < max_children and len(model.get_children(edge[1])) >= max_children and len(model.get_parents(edge[1])) < max_parents:
                        model.add_edge(edge[0],edge[1])

                    if len(model.get_children(edge[0])) >= max_children and len(model.get_children(edge[1])) < max_children and len(model.get_parents(edge[0])) < max_parents:
                        model.add_edge(edge[1],edge[0])

                elif edge[0] in nodes_added and edge[1] not in nodes_added: #Node A in network Node B not in network
                    if len(model.get_children(edge[0])) < max_children:
                        model.add_edge(edge[0],edge[1])
                        nodes_added.append(edge[1])

                elif edge[0] not in nodes_added and edge[1] in nodes_added: #Node A not in network Node B in network
                    if len(model.get_children(edge[1])) < max_children:
                        model.add_edge(edge[1],edge[0])
                        nodes_added.append(edge[0])

                else:
                    #neither in network, choose randomly
                    add_first = random.choice([0,1])
                    model.add_edge(edge[add_first],edge[1-add_first])
                    nodes_added.append(edge[add_first])
                    nodes_added.append(edge[1-add_first])

            except ValueError: #catch non-DAG error
                pass

    #if node is leaf, connect to target
    for node in nodes_added:
        if node in model.get_leaves():
            model.add_edge(node,'target')
            
    return model, nodes_added
Example #2
0
class MyClass(object):
    def __init__(self, case):
        self.case = case
        self.results = []
        self.networx_test = nx.DiGraph()
        self.networx_fixed = nx.DiGraph()
        self.pgmpy_test = BayesianModel()
        self.networx = nx.DiGraph()
        self.pgmpy = BayesianModel()
        self.best_error = math.inf
        self.best_topology = [0, 0, nx.DiGraph,
                              0]  #[error, entropy, networkx DiGraph, loop]
        self.dictionary = []
        self.header = {}
        self.nodes_0 = []
        self.edges_0 = {}
        self.nodes = []
        self.edges = {}
        self.cpds = {}
        self.colors_dictionary = {}
        self.colors_table = []
        self.colors_cpd = []
        self.learning_data = {}
        self.nummber_of_colors = 0
        self._util = Utilities(case)
        self._lat = Lattices(self._util)
        self.expected_result = [0, 0, 0]
        self.loop = 0

    def get_my_colors(self):
        evidence = []
        cardinality = []
        for i, node in enumerate(self.nodes):
            if 'BEN' in node[0] or 'MEM' in node[0]:
                evidence.append(node[0])
                cardinality.append(node[1]['cardinality'])
        self.colors_dictionary, self.colors_table, self.colors_cpd = self.color_cpd(
            'WORLD', 3, evidence, cardinality)
        self.number_of_colors = self.colors_table.shape[1]
        # for i in range(0, len(self.colors_table[1])):
        #     rows = len(self.colors_table)
        #     hi = 1000
        #     lo = 1
        #     sum = hi+(rows-1)
        #     hi /= sum
        #     lo /= sum
        #     for j in range(0, rows):
        #         if self.colors_table[j][i] == 1:
        #             self.colors_table[j][i] = hi
        #         else:
        #             self.colors_table[j][i] = lo

        # print('Number of colors : ', self.number_of_colors)
        # print(self.colors_cpd)
        #print(self.colors_cpd.values)

    def color_cpd(self, var, card_var, evidence, cardinality):
        table = CPD.get_index_matrix(cardinality)
        colors = {}
        hi = 1  #0.999
        lo = 1 - hi
        C = np.prod(cardinality)
        matrix = np.full((3, C), 1. / 3.)
        if 'BENS_1' in evidence and not 'BENS_2' in evidence and 'BENS_3' in evidence and 'BENS_0' in evidence:
            matrix[0] = [1. / 3, lo, hi, 1. / 3, 1. / 3, lo, hi, 1. / 3]
            matrix[1] = [1. / 3, lo, lo, 1. / 3, 1. / 3, lo, lo, 1. / 3]
            matrix[2] = [1. / 3, hi, lo, 1. / 3, 1. / 3, hi, lo, 1. / 3]
        if 'BENS_1' in evidence and not 'BENS_2' in evidence and 'BENS_3' in evidence and not 'BENS_0' in evidence:
            matrix[0] = [1. / 3, lo, hi, 1. / 3]
            matrix[1] = [1. / 3, lo, lo, 1. / 3]
            matrix[2] = [1. / 3, hi, lo, 1. / 3]
        if 'BENS_1' in evidence and 'BENS_2' in evidence and 'BENS_3' in evidence and not 'BENS_0' in evidence:
            matrix[0] = [lo, lo, lo, lo, hi, lo, hi, lo]
            matrix[1] = [hi, lo, hi, lo, lo, hi, lo, hi]
            matrix[2] = [lo, hi, lo, hi, lo, lo, lo, lo]
        if 'BENS_0' in evidence and 'BENS_1' in evidence and 'BENS_2' in evidence and 'BENS_3' in evidence:
            matrix[0] = [
                lo, lo, lo, lo, hi, lo, hi, lo, lo, lo, lo, lo, hi, lo, hi, lo
            ]
            matrix[1] = [
                hi, lo, hi, lo, lo, hi, lo, hi, hi, lo, hi, lo, lo, hi, lo, hi
            ]
            matrix[2] = [
                lo, hi, lo, hi, lo, lo, lo, lo, lo, hi, lo, hi, lo, lo, lo, lo
            ]

        cpd = TabularCPD(variable=var,
                         variable_card=card_var,
                         values=matrix,
                         evidence=evidence,
                         evidence_card=cardinality)
        for i, node in enumerate(evidence):
            colors.update({node: table[i]})
        return colors, table, cpd

    # def set_color(self, color):
    #     col = self.colors_table[:, color]
    #     for i in range(0,len(col)):
    #         node = 'BENS_'+ str(i)
    #         self.pgmpy.get_cpds(node).values = CPD.RON_cpd(node, self.pgmpy.get_cardinality(node), mu = int(col[i])).values

    def test_topology(self, entropy):
        self.networx_test = copy.deepcopy(self.networx)
        self.pgmpy_test = BayesianModel()
        self.pgmpy_test = self._util.translate_digraph_to_pgmpy(
            self.networx.copy())
        #model = {'main': GenerativeModel(SensoryInputVirtualPeepo(self), self.pgmpy_test)}
        self.expected_result = [0, 0, 0]
        ''' ------ going through all possible "colors'''
        error = 0
        for color in range(0, self.number_of_colors):
            states = self.colors_table[:, color]
            shape = self.colors_cpd.values.shape
            reshaped_cpd = self.colors_cpd.values.reshape(
                shape[0], int(np.prod(shape) / shape[0]))
            self.expected_result = reshaped_cpd[:, int(color)]
            for i, pixel in enumerate(states):
                if 'BENS_' + str(i) not in self.networx_test.nodes():
                    continue
                cardinality = self.pgmpy_test.get_cardinality('BENS_' + str(i))
                self.pgmpy_test.get_cpds(
                    'BENS_' + str(i)).values = CPD.create_fixed_parent(
                        cardinality, state=int(pixel))
            #error += self.do_inference(model)

            error += self.do_simple_inference()
        error /= self.number_of_colors
        self.results.append([entropy, error])
        if error <= self.best_error:
            self.best_error = error
            self.best_topology[0] = error
            self.best_topology[1] = entropy
            self.best_topology[2] = self.networx_test
            self.best_topology[3] = self.loop
        self.loop += 1

    def do_inference(self, models):
        error = 0
        for key in models:
            error += models[key].process()
        return error

    '''.................. vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv ..................................'''

    def do_simple_inference(self):
        total_prediction_error_size = 0
        for node in self.pgmpy_test.get_leaves():
            prediction = self.predict(node)
            observation = self.sensory_input(node)
            prediction_error_size = self.error_size(prediction, observation)
            prediction_error = self.error(prediction, observation)
            precision = entropy(prediction, base=2)
            total_prediction_error_size += prediction_error_size
        return total_prediction_error_size

    def predict(self, node):
        """
        Predicts the given leaf node (i.e. the observational node) based on the root nodes (i.e. the belief nodes)
        :return: prediction for given observation variable, a prediction is a probability distribution
        :rtype: np.array
        """
        infer = VariableElimination(self.pgmpy_test)
        evidence = self.get_root_nodes()
        evidence = {k: v for k, v in evidence.items() if k not in [node]}
        return infer.query(variables=[node], evidence=evidence)[node].values

    def sensory_input(self, name):
        expected_result = self.expected_result
        cpds = []
        for i in range(0, len(expected_result)):
            cpds.append([
                'WORLD_' + str(i),
                CPD.create_fixed_parent(2, state=int(expected_result[i]))
            ])
        for i, node in enumerate(self.nodes):
            for j in range(0, len(cpds)):
                if name == cpds[j][0]:
                    return cpds[j][1]

    def error(self, pred, obs):
        """
        Calculates the prediction error as the residual of subtracting the predicted inputs from the observed inputs
        :param pred: predicted sensory inputs
        :param obs: observed sensory inputs
        :return: prediction error
        :type pred : np.array
        :type obs : np.array
        :rtype : np.array
        """
        return obs - pred

    def error_size(self, pred, obs):
        """
        Calculates the size of the prediction error as the Kullback-Leibler divergence. This responds the magnitude
        of the prediction error, how wrong the prediction was.
        :param pred: predicted sensory inputs
        :param obs: observed sensory inputs
        :return: prediction error size
        :type pred : np.array
        :type obs : np.array
        :rtype : float
        """
        return entropy(obs, pred)

    def get_root_nodes(self):
        """
        Returns status of all root nodes.
        :param network: Bayesian Network representing the generative model
        :return: Dictionary containing all root nodes as keys and status as values
        :type network: BayesianModel
        :rtype dict
        """
        roots = {}
        for root in self.pgmpy_test.get_roots():
            roots.update(
                {root: np.argmax(self.pgmpy_test.get_cpds(root).values)})
        return roots

    def get_observations(self):
        obs = {}
        for leaf in self.pgmpy_test.get_leaves():
            obs.update(
                {leaf: np.argmax(self.pgmpy_test.get_cpds(leaf).values)})
        return obs

    '''**********************   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^                           '''

    def estimate_parameters(self):
        data = pd.DataFrame(data=self.learning_data)
        sample_size = len(self.learning_data)
        # print(sample_size)
        estimator = BayesianEstimator(self.pgmpy, data)
        # print('data')
        # print('pgmpy node : ', self.pgmpy.nodes())
        # print(self.learning_data)
        # print(data)
        pseudocount = {
            'BENS_0': [1, 2],
            'BENS_1': [1, 2],
            'BENS_2': [1, 2],
            'BENS_3': [1, 2],
            'WORLD_0': [1, 2],
            'WORLD_1': [1, 2],
            'WORLD_2': [1, 2]
        }

        pseudocount = [0.9, 0.9]
        if not 'BENS_1' in self.pgmpy.nodes(
        ) or not 'BENS_2' in self.pgmpy.nodes(
        ) or not 'BENS_3' in self.pgmpy.nodes():
            pseudocount = [0.9, 0.9, 0.9]
        # print('pseudocount :', pseudocount)
        for i, node in enumerate(self.nodes):

            if 'LAN' in node[0] or 'MOTOR' in node[0] or 'WORLD' in node[0]:
                # print('cardinality node ', node[0], ' : ', self.pgmpy.get_cardinality(node[0]))
                # print(self.pgmpy.get_cpds(node[0]).values)
                #self.pgmpy.get_cpds(node[0]).values = estimator.estimate_cpd(node[0], prior_type='dirichlet', pseudo_counts=pseudocount).values
                self.pgmpy.get_cpds(node[0]).values = estimator.estimate_cpd(
                    node[0],
                    prior_type='BDeu',
                    equivalent_sample_size=sample_size).values

    def add_edges(self, topology):
        self.networx.remove_edges_from(self.edges)
        self.edges = []
        self.nodes = []
        shape = np.asarray(topology).shape
        ''' let's first remove all void nodes  ----> not necssary -----> delete the code ??'''
        nodes_to_remove = []
        # rows = np.sum(topology, axis = 1)
        # for row in range(0, len(rows)):
        #     if rows[row] == 0:
        #         nodes_to_remove.append('WORLD_' + str(row))
        columns = np.sum(topology, axis=0)
        for column in range(0, len(columns)):
            if columns[column] == 0:
                nodes_to_remove.append('BENS_' + str(column))
        self.networx.remove_nodes_from(nodes_to_remove)
        self.nodes = self.networx.nodes(data=True)
        for column in range(0, shape[1]):
            for row in range(0, shape[0]):
                if topology[row][column] == 1:
                    parent = 'BENS_' + str(column)
                    child = 'WORLD_' + str(row)
                    self.networx.add_edge(parent, child)
        self.edges = self.networx.edges()
        # print('edges  --------------------------- >', self.edges)
        # print(self.nodes)

    def add_dummy_cpds(self):
        for i, node in enumerate(self.nodes):
            cardinality = node[1]['cardinality']
            if ('BEN' in node[0]) or ('MEM' in node[0]):
                self.nodes[i][1]['cpd'] = CPD.create_fixed_parent(
                    cardinality, modus='uniform')
            else:
                incoming_nodes = self.networx.in_edges(node[0])
                if len(incoming_nodes) == 0:
                    self.nodes[i][1]['cpd'] = CPD.create_random_child(
                        cardinality, modus='orphan')
                    continue
                card_parent = []
                for m, n in enumerate(incoming_nodes):
                    par = self.networx.node[n[0]]['cardinality']
                    card_parent.append(par)
                self.nodes[i][1]['cpd'] = CPD.create_random_child(
                    cardinality, card_parent)

        # for i, node in enumerate(self.nodes):
        #     print(node[0])
        #     print(node[1]['cpd'])
        self.nodes = self.networx.nodes(data=True)
        # print('   IN NETWORX  ')
        # for i, node in enumerate(self.nodes):
        #     print(node[0])
        #     print(node[1]['cpd'])

    def create_learning_data(self):
        self.get_my_colors()
        self.learning_data = {}
        ben_nodes = [x for x in self.nodes if "BEN" in x[0]]
        world_nodes = [x for x in self.nodes if "WORLD" in x[0]]

        for i, node in enumerate(ben_nodes):
            self.learning_data.update({node[0]: self.colors_table[i].tolist()})

        for i, node in enumerate(world_nodes):
            shape = self.colors_cpd.values.shape
            reshaped_cpd = self.colors_cpd.values.reshape(
                shape[0], int(np.prod(shape) / shape[0]))
            for hue in range(0, 3):
                if str(hue) in node[0]:
                    self.learning_data.update(
                        {node[0]: reshaped_cpd[hue, :].tolist()})
        # for i, node in enumerate(self.nodes):
        #     if "BEN" in node[0]:
        #         self.learning_data.update({node[0]:self.colors_table[i].tolist()})
        #     if "WORLD" in node[0]:
        #         shape = self.colors_cpd.values.shape
        #         reshaped_cpd = self.colors_cpd.values.reshape(shape[0], int(np.prod(shape)/shape[0]))
        #         for hue in range(0,3):
        #             if str(hue) in node[0]:
        #                 self.learning_data.update({node[0]:reshaped_cpd[hue,:].tolist()})
        # print('Learning data')
        # print(self.learning_data)

    def do_it(self):
        '''EXPLANATIONS'''
        self.networx_fixed, self.dictionary, self.header = self._util.get_network(
        )
        self.networx = self.networx_fixed.copy()
        self.networx_test = self.networx_fixed.copy()
        print('Dictionary : ', self.dictionary)
        ''' -------------- Constructing all possible topologies, 
                              --> option : restrain the number with the treshold : 
                                        0 -> all possible topologies, 100 -> only the fully connnected topology'''
        possible_topologies = self._lat.get_possible_topologies(
            treshold=50
        )  #setting the entropy at a 50% -> only topologies with an entropy >= 0.5 will be considered
        print("Possible topologies : ", len(possible_topologies))
        entropy = 0
        count = 0  #TEMPORARY
        ''' -------------- walking through all toplogies'''
        for topology in possible_topologies:
            if self.loop < 200 or self.loop > 350:
                self.loop += 1
                count += 1
                continue
            entropy = topology[1]
            if entropy == 0:
                continue  #safeguard
            print('Loop *-> ', self.loop + 1, ' of ', len(possible_topologies))
            topo = topology[0]
            self.networx = nx.DiGraph()
            self.networx = self.networx_fixed.copy()
            ''' ----------- for each topology we construct the edges and update dummy cpd (necessary as the shape of the LENs cpd's can change
                            depending on the number of incoming nodes'''
            self.add_edges(topo)
            self.add_dummy_cpds()
            self.nodes = self.networx.nodes(data=True)
            self.create_learning_data()
            # print('edges = ' , self.edges)
            #print(self.learning_data)
            ''' ----------- convert DiGraph to pgmpy and check'''
            self.pgmpy = BayesianModel()
            self.pgmpy = self._util.translate_digraph_to_pgmpy(
                self.networx.copy())
            '''------------ ask pgmpy to guess the best cpd's of the LANs and LENs 
                             -> provide pgmpy with the learning data'''

            self.pgmpy.check_model()
            self.estimate_parameters()
            '''-------------- Testing the constructed topology'''
            self.test_topology(entropy)
            '''following  4 lines to remove : just use to check whether the algorithms are correct regarding the edges building'''
            count += 1
            #print('edges : ', self.edges)
            #
            # if count > 350:
            #     break
        print('Check -> number of processed topologies in loop : ', count)
        # print('My colors : ')
        # print(self.colors_table)
        # print(self.colors_cpd)
        '''  the methods have to be completed to cope with a general case i.e. BENS,MEMS,LANS, MOTORs, WORLDs
        but for the moment being we just assume there are only BEN's and WORLD's'''

        # self.networx.add_edge('BENS_1','WORLD_1')
        # self.networx.node['BENS_1']['cpd'] = [0.8,0.2]
        # self.networx.node['WORLD_2']['cpd'] = [[0.8, 0.2, 0.5,0.3],[0.2,0.8,0.5,0.7]]
        ''' if a best model has ben found, save it -> first update the Utility class object and save it'''
        # self._util.update_networkx(self.networx, self.dictionary, self.header)
        # self._util.save_network()
        # self._util.update_pgmpy(self.pgmpy, self.dictionary, self.header)
        # self._util.save_pgmpy_network()
        self.draw()
        self.draw_xy()
        return self.results

    def draw_xy(self):
        x = []
        y = []
        s = []
        color = []
        best_x = 0
        best_y = 0
        for i in range(0, len(self.results)):
            x.append(self.results[i][0])
            y.append(self.results[i][1])
            if i == self.best_topology[3]:
                best_x = self.results[i][0]
                best_y = self.results[i][1]
                s.append(60)
                color.append("r")
            else:
                s.append(20)
                color.append("b")
        plt.scatter(x, y, s=s, c=color, alpha=0.5)
        plt.xlabel("Complexity of topology")
        plt.ylabel("Average error over all colors")
        plt.show()

    def draw(self):
        '''TO REMOVE LATER'''
        plt.figure(figsize=(10, 5))
        pos = nx.circular_layout(self.best_topology[2], scale=2)
        #node_labels = nx.get_node_attributes(self.networx, 'cpd')
        nx.draw(self.best_topology[2],
                pos,
                node_size=1200,
                node_color='lightblue',
                linewidths=0.25,
                font_size=10,
                font_weight='bold',
                with_labels=True)
        plt.text(1, 1, 'Topology nr. : ' + str(self.best_topology[3]))
        plt.show()
Example #3
0
obs_traj = None
# sample_generator(generator, obs_traj)
path_node.transition = None
noisy_path_node.transition = None

# CREATING BAYESIAN MODEL (GRAPH)
cusumano_model = BayesianModel([(start_node, path_node),
                                (goal_node, path_node),
                                (path_node, noisy_path_node)])

# ATTENTION!!! =)
# Test functions. If these do not work you probably need to check python requirements.txt:
# this was tested using networkx 2.3 and pgmpy 0.1.7 from dev branch in github.
print("Root nodes: %s" % cusumano_model.get_roots())
print("Children of 'start': %s" % cusumano_model.get_children(start_node))
print("Leaf nodes: %s" % cusumano_model.get_leaves())
print("Parents of 'noisy_path': %s" %
      cusumano_model.get_parents(noisy_path_node))
# FIXME: not working: we get error 'RandomChoice' object is not iterable'.
# print(cusumano_model.get_independencies())

nx.draw_networkx(cusumano_model,
                 arrowsize=15,
                 node_size=800,
                 node_color='#90b9f9')
plt.show()


# Defines whether to accept or reject the new sample
def acceptance(x, x_new):
    if x_new > x: