Exemplo n.º 1
0
class TestHypergraph(unittest.TestCase):
    def setUp(self):
        self.a = Hypergraph()

    def test_checkUnknownNode(self):
        self.assertRaises(ValueError,
                          self.a.updateNode,
                          "z",
                          2)

    def test_checkUnknownHyperedge(self):
        self.assertRaises(ValueError,
                          self.a.updateHyperedgeLabel,
                          ("z", "s"),
                          2, 0)

    def test_addNode1(self):
        self.a.addNode('a', 1)

        self.assertEqual(self.a.getNodeWeight('a'), 1)

    def test_addNode2(self):
        self.a.addNode('a', 1)
        self.a.addNode('b', 2)

        self.assertEqual(self.a.getNodeWeight('a'), 1)
        self.assertEqual(self.a.getNodeWeight('b'), 2)

    def test_addNode3(self):
        self.a.addNode('a', 1)
        self.a.addNode('b', 2)
        self.a.addNode('c', 3)

        self.assertEqual(self.a.getNodeWeight('a'), 1)
        self.assertEqual(self.a.getNodeWeight('b'), 2)
        self.assertEqual(self.a.getNodeWeight('c'), 3)

    def test_updateNodeValue(self):
        self.a.addNode('a', 1)
        self.a.addNode('b', 2)

        self.a.updateNode('a', 3)

        self.assertEqual(self.a.getNodeWeight('a'), 3)

    def test_addHyperedge1(self):
        he = ('a', 'b', 'c')

        self.a.addNode('a', 1)
        self.a.addNode('b', 2)
        self.a.addNode('c', 3)

        self.a.addHyperedge(he, "abc", 0)

        self.assertEqual(self.a.getHyperedgeLabel(he).data, 
                         "abc")

    def test_addHyperedge2(self):
        he = ('a', 'b', 'c')
        he2 = ('d', 'b', 'c')

        self.a.addNode('a', 1)
        self.a.addNode('b', 2)
        self.a.addNode('c', 3)
        self.a.addNode('d', 4)

        self.a.addHyperedge(he, "abc", 0)
        self.a.addHyperedge(he2, "dbc", 0)

        self.assertEqual(self.a.getHyperedgeLabel(he2).data,
                         "dbc")

    def test_updateHyperedgeLabel(self):
        he = ('a', 'b', 'c')
        he2 = ('d', 'b', 'c')

        self.a.addNode('a', 1)
        self.a.addNode('b', 2)
        self.a.addNode('c', 3)
        self.a.addNode('d', 4)

        self.a.addHyperedge(he, "abc", 0)
        self.a.addHyperedge(he2, "dbc", 0)
        self.a.updateHyperedgeLabel(he, "test", 0)

        self.assertEqual(self.a.getHyperedgeLabel(he).data,
                         "test")

    def test_checkHyperedgesAndNodes(self):
        he = ('a', 'b', 'c')
        he2 = ('d', 'b', 'c')
        solution = [he, he2]

        self.a.addNode('a', 1)
        self.a.addNode('b', 2)
        self.a.addNode('c', 3)
        self.a.addNode('d', 4)

        self.a.addHyperedge(he, "abc", 0)
        self.a.addHyperedge(he2, "dbc", 0)

        self.assertEqual(self.a.getHyperedgesFromNode('b'), solution)
Exemplo n.º 2
0
class DirectedAcyclicGraphComparator:
    """
    The class perfoms the comparation between two different
    DirectedAcyclicGraphs

    To use it call the function buildHyperGraph, it will return a hypergraph
    containing the comparision between the two dags. The contents of the
    hypergraph will be as follows:
        nodes: formed by one node of each graph storing the value of applying
               the cost function to both nodes.
        hyperedges: hyperedges are directed, the first node of the hyperedge is
                    the source node of the transformation, the rest of the
                    nodes of the hyperedges will contain the location of the
                    variables. As a value it will store the sum of the cost of
                    the variables plus applying the transformation function to
                    the graphs without the nodes being substituted. Each
                    hyperedge must be different.
    """
    def __init__(self, dag1, dag2):
        """
        The first and second parameters must be DirectedAcyclicGraphs as
        specified on the file datastructures.py"
        """

        self.dag1_mapper = DirectedAcyclicGraphMapper(dag1)
        self.dag2_mapper = DirectedAcyclicGraphMapper(dag2)
        self.hypergraph = Hypergraph()

    def costAssembler(self, functions):
        pass

    def __sort_by_num_of_variables(self, v):
        max_num_of_variables = max(map(lambda x: len(x.variables), v))
        answers = tuple([[] for _ in xrange(max_num_of_variables)])

        for x in v:
            answers[len(x.variables) - 1].append(x)

        return answers

    def __iterate_over_sorted_maps(self, s1, s2):
        for x1, x2 in zip(s1, s2):
            for map1 in x1:
                for map2 in x2:
                    yield (map1, map2)

    def buildHyperGraph(self, number_of_variables=float('inf')):
        """
        This function builds the hypergraph that will contain the comparision
        between the two dags and all its subgraphs

        The function returns a hypergraph containing the comparision between
        the two dags.
        """

        # Compute the nodes of the hypergraph and its associated cost. Each
        # node is formed by each possible pair created using two random nodes
        # of each dag.
        g1 = self.dag1_mapper.dag
        g2 = self.dag2_mapper.dag
        for n1 in self.dag1_mapper.dag.links.iterkeys():
            for n2 in self.dag2_mapper.dag.links.iterkeys():
                # value = t_cost_function_distance([n1], [n2])
                value = t_cost_edit_distance_graphs_no_vars(g1, n1, g2, n2)
                self.hypergraph.addNode((n1, n2), value)

        # In the algorithm we don't allow to compute the cost function between
        # two subgraphs with different number of variables. Here
        # we sort both sequences of subgraphs by its number of variables to
        # assure that doesn't happen.
        map1_sorted_by_vars = self.__sort_by_num_of_variables(
            self.dag1_mapper.generateAllVariableMappings(
                number_of_variables=number_of_variables))
        map2_sorted_by_vars = self.__sort_by_num_of_variables(
            self.dag2_mapper.generateAllVariableMappings(
                number_of_variables=number_of_variables))

        # Thanks to its ordering coming from the Mapper class the hypergraph
        # will be built on a top down fashion.
        # map1 and map2 will always contain the same number of variables.
        for map1, map2 in self.__iterate_over_sorted_maps(
                map1_sorted_by_vars, map2_sorted_by_vars):
            # This variable will contain the total coming from the
            # substituted variables.
            # total_from_variables = 0.0

            # The node of the hypergraph.
            hypergraph_node = (map1.subgraph.root, map2.subgraph.root)

            # The current hyperedge, on this implementation the order
            # matters the first node will be the node acting as a root
            # and the rest the nodes that are going to be substituted
            # by variables.
            hyperedge = (hypergraph_node, ) + tuple(
                zip(map1.variables, map2.variables))

            # The cost of the node of the hypergraph.
            # f1 = t_cost_function([map1.subgraph.root],
            #                      [map2.subgraph.root])
            f1 = t_cost_edit_distance_graphs_with_vars(map1, map2)

            # This is for debuging pourposes
            if DEBUG_MODE:
                print stringifyGraph(map1.graph, map1.subgraph.root,
                                     map1.variables, map1.subgraph.nodes)
                print stringifyGraph(map2.graph, map2.subgraph.root,
                                     map2.variables, map2.subgraph.nodes)

                print 'Hyperedge', hyperedge

            # Obtain the accumulated value for the variables involved on
            # the substitution.
            # for n1, n2 in zip(map1.variables, map2.variables):
            #     if DEBUG_MODE:
            #         print 'Querying:', n1, n2
            #     total_from_variables += self.hypergraph.getNodeWeight((n1,
            #                                                            n2))

            # Add the hyperedge to the graph
            # The hyperedges are directed and as the algorithm works
            # there should't be any duplicates so there is no need to
            # check if it exists.
            subgraphs = (map1.subgraph, map2.subgraph)
            # weight = f1 + total_from_variables
            weight = f1
            self.hypergraph.addHyperedge(hyperedge, subgraphs, weight)

            # Check if with the values we have computed we have to update
            # value of the node.
            # if (f1 + total_from_variables) > \
            #    self.hypergraph.getNodeValue(hypergraph_node):
            #     self.hypergraph.updateNode(hypergraph_node,
            #                                (f1 + total_from_variables))

            # if DEBUG_MODE:
            #     print 'Partial graph value', f1
            #     print 'Variables value', total_from_variables
            #     print "=========================="

        if DEBUG_MODE:
            print "\nNodes:"
            print "=========================="
            self.hypergraph.printNodes()
            print "\nHyperedges:"
            print "=========================="
            self.hypergraph.printHyperedges()

    def buildHyperGraphDebug(self, number_of_variables=float('inf')):
        """
        Debugging function that uses the default computing cost function
        to build the hypergraph.

        Used for testing purposes.
        """

        for n1 in self.dag1_mapper.dag.links.iterkeys():
            for n2 in self.dag2_mapper.dag.links.iterkeys():
                value = t_cost_default([n1], [n2])
                self.hypergraph.addNode((n1, n2), value)

        map1_sorted_by_vars = self.__sort_by_num_of_variables(
            self.dag1_mapper.generateAllVariableMappings(
                number_of_variables=number_of_variables))
        map2_sorted_by_vars = self.__sort_by_num_of_variables(
            self.dag2_mapper.generateAllVariableMappings(
                number_of_variables=number_of_variables))

        for map1, map2 in self.__iterate_over_sorted_maps(
                map1_sorted_by_vars, map2_sorted_by_vars):
            hypergraph_node = (map1.subgraph.root, map2.subgraph.root)
            hyperedge = (hypergraph_node, ) + tuple(
                zip(map1.variables, map2.variables))
            weight = t_cost_default(map1.subgraph.nodes, map2.subgraph.nodes)

            subgraphs = (map1.subgraph, map2.subgraph)
            self.hypergraph.addHyperedge(hyperedge, subgraphs, weight)