예제 #1
0
def find_perfect_matching(graph, match=None):
    '''
    find perfect matching with max cardinality on unweighted undirected graph

    :param graph: UGraph object, not compatible with weighted UGraph
    :param match: UGraph object, default None at the beginning
    :returns: UGraph object, self.edge attribute give the mathcing edge tuple list
    '''
    if match is None:
        match = UGraph([])
    path = find_aug_path(graph, match)
    if not path:
        return match
    else:
        new_edges = match.edge
        logger.info('find augment path as %s'%path)
        for i in range(len(path) - 1):
            if i % 2 == 0:
                new_edges.append((path[i].name, path[i + 1].name))
            else:
                if path[i].name < path[i+1].name:
                    new_edges.remove((path[i].name, path[i+1].name))
                else:
                    new_edges.remove((path[i+1].name, path[i].name))
        match = UGraph(new_edges)
        return find_perfect_matching(graph, match)
예제 #2
0
    def __init__(self, graph):
        self.graph = graph
        self.n = len(graph.vertex)  # number of vertex of original graph

        if self.n == 0:
            logger.warning('Empty graph')
            maxweight = 0
        else:
            maxweight = max(graph.rep, key=lambda x: x[2])[2]
        self.m = len(graph.edge)  # number of edge of original graph

        self.edge = graph.edge
        self.weight = {(e[0], e[1]): e[2]
                       for e in graph.rep}  # edge weight correspondence

        self.z = {i: maxweight / 2
                  for i in graph.vertex
                  }  # valid for all vertex v and blossom in any level
        # blossom is indexed by the tuple rep, which is stored in self.status
        self.status = {i: [(i, )] for i in graph.vertex}
        self.contain = {
            i: [i]
            for i in graph.vertex
        }  # valid for top v, for v not in top level, its elements are for diff levels
        # by status together with contain, we can know the over all structure of the nested blossoms
        # status: {1: [(1,),(1,2),(3,1)], 2: [(2,),(1,2),(3,1)], 3:[(3,),(3,1)]}, each item of dict track the blossoms from bottom to top
        # contain: {1:[1,2,3,4], 2:[3,4]...} each item of dict track all basic vertex contained in some high level effective vertex

        self.match = UGraph([])  # relative matching on top level
        self.rgraph = UGraph(self.graph.edge)  # reduced graph
        # quantities below clear every phase
        self.stf = {i: 'F'
                    for i in graph.vertex
                    }  # valid for all real v, no matter level it is
        self.stfb = {
            i: None
            for i in graph.vertex
        }  # valid for top blossom, labeled as v, None stands no blossom in v
        self.parent = {i: None
                       for i in graph.vertex
                       }  # valid for top level, b labeled as v
        self.mark = {i: False
                     for i in graph.edge
                     }  # defined on edge of top level, relative mark
        self.checkq = []  # check queue

        self.static_status = None  # desinged for keep blossom info for the final all-expansion
예제 #3
0
 def graph_update(
         self):  # reconstruct the rgraph based on current self.status
     graph_edges = set()
     for e in self.graph.edge:
         newe = self.edge_tonew(e)
         if newe is not None:
             graph_edges.add(newe)
     self.rgraph = UGraph(list(graph_edges))
예제 #4
0
 def alt_path(self, path):
     logger.info('the augmented path to be alternated: %s' % path)
     new_edges = self.match.edge
     for i in range(len(path) - 1):
         if i % 2 == 0:
             new_edges.append((path[i], path[i + 1]))
         else:
             new_edges.remove(edge_reg((path[i], path[i + 1])))
     self.match = UGraph(new_edges)
예제 #5
0
def find_maximum_matching(graph_edges, debug=False):
    '''
    find match with maximum total weights on undirected weighted graph

    :param graph_edges: edge list in the form : [(2,3,6),(5,4,3),(4,2,7)], in each tuple, the first two elements are edge
        end vertex, and the last term is the weight of corresponding edge
    :param debug: boolean, default False, if set as True, the results would be check based on the theorem
    :returns: UGraph object, self.edge attribute give the mathcing edge tuple list
    '''
    st = DressedGraph(UGraph(graph_edges))
    st.phases()
    if debug is True:
        st.check_optimum()
    return st.match
예제 #6
0
def find_perfect_maximum_matching(graph_edges):
    '''
    find perfect matching with maximum total weights on undirected weighted graph

    :param graph_edges: edge list in the form : [(2,3,6),(5,4,3),(4,2,7)], in each tuple, the first two elements are edge
       end vertex, and the last term is the weight of corresponding edge
    :param debug: boolean, default False, if set as True, the results would be check based on the theorem
    :returns: UGraph object, self.edge attribute give the mathcing edge tuple list
    '''
    weights_sum = sum(e[2] for e in graph_edges)
    reweight_graph = []
    for e in graph_edges:
        reweight_graph.append((e[0], e[1], e[2] + weights_sum))
    st = DressedGraph(UGraph(reweight_graph))
    st.phases()
    return st.match
예제 #7
0
def reduce_graph(graph, btuple):
    new_edges = []
    blossom_ele = list(btuple)
    for e in graph.edge:
        ee = list(e)
        if (e[0] in blossom_ele) and (e[1] not in blossom_ele):
            ee[0] = btuple[0]
            if edge_reg(tuple(ee)) not in new_edges:
                new_edges.append(edge_reg(tuple(ee)))
        elif (e[1] in blossom_ele) and (e[0] not in blossom_ele):
            ee[1] = btuple[0]
            if edge_reg(tuple(ee)) not in new_edges:
                new_edges.append(edge_reg(tuple(ee)))
        elif (e[1] in blossom_ele) and (e[0] in blossom_ele):
            pass
        else:
            if e not in new_edges:
                new_edges.append(e)
    return UGraph(new_edges)
예제 #8
0
def reduce_graph(graph, blossom):
    new_edges = []
    blossom_ele = [m.name for m in blossom.loop]
    for e in graph.edge:
        ee = list(e)
        if (e[0] in blossom_ele) and (e[1] not in blossom_ele):
            ee[0] = blossom.base.name
            if tuple(ee) not in new_edges:
                new_edges.append(tuple(ee))
        elif (e[1] in blossom_ele) and (e[0] not in blossom_ele):
            ee[1] = blossom.base.name
            if tuple(ee) not in new_edges:
                new_edges.append(tuple(ee))
        elif (e[1] in blossom_ele) and (e[0] in blossom_ele):
            pass
        else:
            if e not in new_edges:
                new_edges.append(e)
    logger.debug('new edges list in the reduced graph or match: %s' % new_edges)
    graphr = UGraph(new_edges)
    return graphr
예제 #9
0
    def expand_blossom(self,
                       vrep,
                       delz=True):  # v is the rep of the blossom at top level
        # delz is False for final expand-all
        if self.blossom_level(vrep) == 0:  # compatible with vertex expansion
            return None
        btuple = self.status[vrep][-1]
        logger.debug('expand blossom with vertex contained: %s' % list(btuple))
        # check whether there is a child attached to this blossom
        child = None
        for v in self.top_vertice():
            if vrep == self.parent[v]:
                child = v
                break

        logger.debug('z: %s' % self.z)  #
        # logger.debug('the status:%s'% self.status)
        if delz is True:
            del self.z[btuple]
        for v in self.contain[btuple[0]]:
            self.status[v].pop(-1)
        for v in btuple[1:]:
            for u in self.contain[v]:
                self.contain[vrep].remove(u)

        logger.debug('the status:%s' % self.status)
        logger.debug('the contain:%s' % self.contain)
        self.stfb[vrep] = None

        self.graph_update()  # expand the graph

        # update parent and stf(b)
        father = self.parent[vrep]
        for u in btuple:
            self.parent[u] = None
            self.stf_set(u, 'F')
        if father is not None:
            for ve in self.rgraph.adj[father]:
                if ve in btuple and self.is_equal(edge_reg(
                    (ve, father))):  # or (delz is False)
                    start = ve
                    break
            if child is None:

                self.parent[start] = father
                self.stf_set(start, 'T')
            else:

                for ve in self.rgraph.adj[child]:
                    if ve in btuple and self.is_equal(edge_reg(
                        (ve, child))):  #or (delz is False)
                        end = ve
                        break
                self.parent[child] = end
                # sid = btuple.index(start)
                eid = btuple.index(end)
                blist = list(btuple)

                blist = list_shift(blist, len(blist) - eid)  # eid at index 0
                # logger.debug('the  blist: %s' % blist)  #
                sid = blist.index(start)
                eid = blist.index(end)
                if (eid - sid) % 2 == 1:
                    blist.reverse()
                    blist = list_shift(blist, 1)
                    # logger.debug('the blist: %s' % blist)  #
                logger.debug('the start, end, and the blist: %s,%s,%s' %
                             (start, end, blist))  #
                for j, ve in enumerate(blist):

                    if ve == start:
                        self.parent[ve] = father
                        logger.debug('set %s parent as %s' % (ve, father))
                        self.stf_set(ve, 'T')
                        break
                    else:
                        self.parent[ve] = blist[j + 1]
                        logger.debug('set %s parent as %s' %
                                     (ve, blist[j + 1]))
                        if j % 2 == 0:
                            self.stf_set(ve, 'T')
                        else:
                            self.stf_set(ve, 'S')

        logger.debug('outermost stf of blossoms: %s' % self.stfb)
        logger.debug('parent state after blossom exposion: %s' % self.parent)

        # update mark
        new_mark = {}
        for e in self.rgraph.edge:
            if e in self.mark and (e[0] not in btuple) and (e[1]
                                                            not in btuple):
                new_mark[e] = self.mark[e]
            else:
                if (self.parent[e[0]] == e[1]) or (self.parent[e[1]] == e[0]):
                    new_mark[e] = True
                else:
                    new_mark[e] = False
        self.mark = new_mark
        # logger.debug('mark state after blossom expansion: %s'%self.mark)

        new_edges = self.match.edge
        # update relative match
        # check is there is a match line:
        blist = list(btuple)
        if vrep in self.match.vertex:
            outer = self.match.adj[vrep][0]
            for member in btuple:
                #logger.debug('member %s, outer %s, in edges %s, equal %s'%(member, outer, edge_reg((member, outer)) in self.rgraph.edge
                #  ,self.is_equal(edge_reg((member, outer)))))#
                if (edge_reg((member, outer)) in self.rgraph.edge):
                    # if delz is False:
                    #     logger.debug('member %s, outer %s, equal %s, blossom included %s'
                    #                  %(member, outer,self.is_equal(edge_reg((member, outer))), self.blossoms_cover_edge(edge_reg((member, outer))) ))#
                    if (
                            self.is_equal(edge_reg((member, outer)))
                    ):  #or (not delz): # delz is in last phase, there is nonzero z
                        # blossom to be expanded and hence is_euqal doesn't work
                        inner = member
                        if self.parent[outer] == member:
                            break
            # logger.debug('remove edge (%s,%s) from new match'%(vrep, outer))
            new_edges.remove(edge_reg((vrep, outer)))
            # logger.debug('add edge (%s,%s) into new match' % (inner, outer))
            new_edges.append(edge_reg((inner, outer)))
            # choose match edges within blossom based on matched (inner,outer) edge

            rem = btuple.index(inner)
            # logger.debug('inner point and its index is: %s, %s' %(inner, rem))
            blist = list_shift(blist, len(btuple) - rem)
            # logger.debug('blossom loop from base point: %s'%blist)
        for i, _ in enumerate(blist):
            if i % 2 == 1:
                new_edges.append(edge_reg((blist[i], blist[i + 1])))
        self.match = UGraph(new_edges)
        logger.debug('ending of expansion blossom: %s' % list(btuple))