Пример #1
0
def forest_isomorphism(f1, f2):
    #TODO: do this at a higher level, since both iso testers need it
    if sum(1 for v in f1.vertices()) != sum(1 for v in f2.vertices()) or \
       sum(1 for e in f1.edges()) != sum(1 for e in f2.edges()):
        return None

    results = []
    for forest in (f1,f2):
        generator = depth_first_generator(forest)
        visitor = AggregateVisitor(backend=forest, 
                                   visitors={Depth:None}) 
        traverse(root_vertices=graph.vertices(),
                 visitor=visitor,
                 generator=depth_first_generator(forest))
        results.append(visitor.Depth)
    f1_depth, f2_depth = results

    f1_by_depths = ydict(backend=f1)
    f2_by_depths = ydict(backend=f2)
    max_depth = 0
    for forest, depth, index_by_depths in [(f1, f1_depth, f1_by_depths),
                                           (f2, f2_depth, f2_by_depths)]:
        for v in forest.vertices():
            d = depth[v]
            max_depth = max(d, max_depth)
            index_by_depths[d] = index_by_depths.setdefault(d, ydeque(backend=forest)).append(v)
            
    prev_f1_canonical_id = ydict(backend=f1)
    prev_f2_canonical_id = ydict(backend=f2)
    if len(f1_by_depths[max_depth]) != len(f2_by_depths[max_depth]):
        return
    for v in f1_by_depths[max_depth]:
        prev_f1_canonical_id[v] = 0
    for v in f2_by_depths[max_depth]:
        prev_f2_canonical_id[v] = 0

    for depth in xrange(max_depth - 1, -1, -1):
        f1_vertex_id = ydict(backend=f1)
        f2_vertex_id = ydict(backend=f2)
        if len(f1_by_depths[depth]) != len(f2_by_depths[depth]):
            return
        # don't do two-phase canonical id -> id transformation. instead, compute canonical ids
        # on the fly by storing a lexicographically sorted list of ids and binary searching for
        # existing ids each time
        for v in f1_by_depths[depth]:
            f1_vertex_id[v] = ydeque(backend=f2_vertex_id,
                                     ysorted(backend=f2_vertex_id,
                                             (prev_f1_canonical_id[w] for u,w in f1.edges(source=v))))
        for v in f2_by_depths[depth]:
            f2_vertex_id[v] = ydeque(backend=f2_vertex_id,
                                     ysorted(backend=f2_vertex_id,
                                             (prev_f2_canonical_id[w] for u,w in f2.edges(source=v))))
Пример #2
0
def isomorphism(g1, g2):
    if sum(1 for v in g1.vertices()) != sum(1 for v in g2.vertices()) or \
       sum(1 for e in g1.edges()) != sum(1 for e in g2.edges()):
        return None

    test_funcs = [partial(reachability_signature, reach=1),
                  partial(reachability_signature, reach=2),
                  partial(reachability_signature, reach=4)]
    g1cf = canonical_form(g1, test_funcs)
    g2cf = canonical_form(g2, test_funcs)

    sorted_g1cf = ysorted(g1, g1cf.itervalues())
    sorted_g2cf = ysorted(g2, g2cf.itervalues())

    for x,y in izip(sorted_g1cf, sorted_g2cf):
        if x != y:
            return None

    g2_cform_inv = function_inverse(g2cf, g2)

    iso = ydict(backend=g1)

    contexts = ydeque(backend=g1)
    for v in g1.vertices():
        g2_inv_image = ydeque(backend=contexts)
        for image_element in g2_cform_inv[g1cf[v]]:
            g2_inv_image.append(image_element)
        contexts.append((v, g2_inv_image))
    index = 0

    while index >= 0 and index < len(contexts):
        vertex = contexts[index][0]
        if contexts[index][1]:
            v_prime = contexts[index][1].popleft()
            iso[vertex] = v_prime
            if is_a_partial_isomorphism(g1, g2, iso):
                index += 1
        else:
            if vertex in iso:
                del iso[vertex]
            g2_inv_image = ydeque(backend=contexts)
            for image_element in g2_cform_inv[g1cf[vertex]]:
                g2_inv_image.append(image_element)
                (index, vertex, str([x for x in g2_inv_image]))
            contexts[index] = (vertex, g2_inv_image)
            index -= 1

    if index < 0:
        return None
    else:
        return iso
Пример #3
0
def make_tree(graph, source, edge_weight = None):
    parent = ydict(backend=graph)
    distance = ydict(backend=graph)
    queue = ydeque(backend=graph)
    queue_members = ydict(backend=graph)

    queue.append(source)
    queue_members[source] = True
    distance[source] = 0
    curr_pass = 0
    curr_last = source

    num_vertices = sum(1 for v in graph.vertices())

    while queue:
        v = queue.popleft()
        del queue_members[v]
        for e in graph.edges(source = v):
            w = other_vertex(e, v)
            dw, dv, e_weight = distance.get(w), distance[v], edge_weight[e]
            if dw is None or dv + e_weight < dw:
                distance[w] = dv + e_weight
                parent[w] = e
                if w not in queue_members:
                    queue.append(w)
                    queue_members[w] = True
        if v == curr_last and queue:
            curr_pass += 1
            curr_last = queue[-1]
        if curr_pass >= num_vertices:
            raise NegativeCycleError
    return parent
Пример #4
0
    def __init__(self, graph):
        self.description = 'depth-first search topological sort'
        finished_vertices = ydict(backend=graph)
        generator = depth_first_generator(graph)

        # Bootstrap the depth-first search generator with all vertices
        # of indegree 0. If there are no such vertices, raise an exception.
        found_starting_vertex = False
        for v in graph.vertices():
            try:
                graph.edges(target=v).next()
            except StopIteration:
                generator.bootstrap(v)
                found_starting_vertex = True
        if not found_starting_vertex:
            raise NoIndegreeZeroVertices

        # Topological ordering is now the reverse of the postorder. But
        # a topological ordering still might not be possible if there are
        # cycles, so we'll check for those here and raise an exception
        # if there is one.
        self.topological_order = ydeque(backend=graph)
        # TODO: don't use the generator directly here, use a visitor
        for e in generator.events():
            if e['type'] == 'VERTEX' and e['state'] == 'FINISHED':
                self.topological_order.append(e['vertex'])
            elif e['type'] == 'EDGE' and e['target_state'] == 'DISCOVERED':
                raise CycleExists
Пример #5
0
def reachability_signature(g, reach):
    sig = ydict(backend=g)
    for v in g.vertices():
        sig[v] = ydeque(sig, [1])
    for i in range(reach):
        for u in g.vertices():
            val = sum(sig[v][i] for x,v in g.edges(source=u))
            sig[u].append(val)
    return ydict(g, ((k,v[-1]) for k,v in sig.iteritems()))
Пример #6
0
def function_inverse(f, backend):
    inverse = ydict(backend=backend)
    for key, value in f.iteritems():
        inverse.setdefault(value,ydeque(backend=inverse)).append(key)
    return inverse
Пример #7
0
 def __init__(self, graph):
     base_generator.__init__(self, graph)
     self.__deque = ydeque(backend=graph)
Пример #8
0
 def __init__(self, agg):
     CompositeVisitor.__init__(self, agg)
     self.S = ydeque(backend=agg)
     self.P = ydeque(backend=agg)
     self.next_component_id = 0