Ticket #8922: trac_8922.patch

File trac_8922.patch, 12.8 KB (added by ncohen, 11 years ago)
  • sage/graphs/base/dense_graph.pxd

    # HG changeset patch
    # User Nathann Cohen <nathann.cohen@gmail.com>
    # Date 1273258872 14400
    # Node ID 5a688fdc03a3cede3566d5335d68eddd317c5099
    # Parent  decbd57f6680a443348813d94c3eb7d94ae6e199
    Trac 8922 : induced_subgraph_search in Graph
    
    diff -r decbd57f6680 -r 5a688fdc03a3 sage/graphs/base/dense_graph.pxd
    a b  
    2121    cdef int radix_mod_mask
    2222    cdef int num_longs
    2323    cdef unsigned long *edges
     24    cdef int * adjacency_sequence(self, int n, int * vertices, int vertex)
    2425
    2526    # Method declarations inherited from CGraph:
    2627    # cdef int add_arc_unsafe(self, int, int)
  • sage/graphs/base/dense_graph.pyx

    diff -r decbd57f6680 -r 5a688fdc03a3 sage/graphs/base/dense_graph.pyx
    a b  
    456456        self.check_vertex(u)
    457457        self.check_vertex(v)
    458458        self.del_arc_unsafe(u,v)
     459
     460    cdef int * adjacency_sequence(self, int n, int * vertices, int vertex):
     461        r"""
     462        Returns the adjacency sequence corresponding to a list of
     463        vertices, and a vertex
     464       
     465        INPUT:
     466
     467        - ``n`` -- number of elements in ``vertices``
     468        - ``vertices`` -- list of vertices
     469        - ``vertex``
     470
     471        OUTPUT:
     472
     473        Returns a list of ``n`` integers, whose i th element
     474        is set to 1 iif vertex is adjacent to vertices[i]
     475        """
     476
     477        cdef int i = 0
     478        cdef int * line
     479        line = <int *> sage_malloc(n*sizeof(int))
     480        for 0 <= i < n:
     481            line[i] = 1 if self.has_arc_unsafe(vertices[i],vertex) else 0
     482
     483        return line
    459484   
    460485    ###################################
    461486    # Neighbor functions
  • sage/graphs/generic_graph.py

    diff -r decbd57f6680 -r 5a688fdc03a3 sage/graphs/generic_graph.py
    a b  
    42104210        Same test with the Linear Program formulation::
    42114211
    42124212           sage: g = graphs.PappusGraph()
    4213            sage: g.matching(algorithm="LP", value_only=True)
     4213           sage: g.matching(algorithm="LP", value_only=True)  # optional - requires GLPK CBC or CPLEX
    42144214           9.0
    42154215
    42164216        TESTS:
     
    69116911        G.delete_edges(edges_to_delete)
    69126912        if not inplace:
    69136913            return G
     6914
     6915    def induced_subgraph_search(self, G):
     6916        r"""
     6917        Returns an induced copy of `G` in self.
     6918
     6919        INPUT:
     6920
     6921        - ``G`` -- the graph whose copy we are looking for in ``self``
     6922
     6923        ALGORITHM:
     6924
     6925        Brute-force
     6926
     6927        EXAMPLES:
     6928   
     6929        A Petersen Graph contains a `P_5` ::
     6930   
     6931             sage: g = graphs.PetersenGraph()
     6932             sage: h1 = g.induced_subgraph_search(graphs.PathGraph(5))
     6933             sage: h1
     6934             Subgraph of (Petersen graph): Graph on 5 vertices
     6935             sage: h1.vertices()
     6936             [0, 1, 2, 3, 8]
     6937   
     6938        It also contains a Claw (`K_{1,3}`)::
     6939   
     6940             sage: h2 = g.induced_subgraph_search(graphs.ClawGraph())
     6941             sage: h2
     6942             Subgraph of (Petersen graph): Graph on 4 vertices
     6943             sage: h2.vertices()
     6944             [0, 1, 4, 5]
     6945
     6946        Of course both copies are isomorphic to the graphs we were looking
     6947        for ::
     6948
     6949             sage: h1.is_isomorphic(graphs.PathGraph(5))
     6950             True
     6951             sage: h2.is_isomorphic(graphs.ClawGraph())
     6952             True
     6953
     6954        However, as it contains no induced subgraphs isomorphic to
     6955        `P_6`, an exception is raised in this case ::
     6956   
     6957             sage: g.induced_subgraph_search(graphs.PathGraph(6))
     6958             Traceback (most recent call last):
     6959             ...
     6960             ValueError: No induced copy of the graph exists
     6961        """
     6962        from sage.graphs.generic_graph_pyx import subgraph_search
     6963
     6964        H = subgraph_search(self, G, induced = True)
     6965
     6966        if H == []:
     6967            raise ValueError('No induced copy of the graph exists')
     6968       
     6969        return self.subgraph(H)
     6970
     6971    def subgraph_search(self, G):
     6972        r"""
     6973        Returns an of `G` included in self.
     6974
     6975        INPUT:
     6976
     6977        - ``G`` -- the graph whose copy we are looking for in ``self``
     6978
     6979        ALGORITHM:
     6980
     6981        Brute-force
     6982
     6983        EXAMPLES:
     6984   
     6985        A Petersen Graph contains a `P_5` ::
     6986   
     6987             sage: g = graphs.PetersenGraph()
     6988             sage: h1 = g.subgraph_search(graphs.PathGraph(5))
     6989             sage: h1
     6990             Subgraph of (Petersen graph): Graph on 5 vertices
     6991             sage: h1.vertices()
     6992             [0, 1, 2, 3, 4]
     6993   
     6994        It also contains a Claw (`K_{1,3}`)::
     6995   
     6996             sage: h2 = g.subgraph_search(graphs.ClawGraph())
     6997             sage: h2
     6998             Subgraph of (Petersen graph): Graph on 4 vertices
     6999             sage: h2.vertices()
     7000             [0, 1, 4, 5]
     7001
     7002        However, as it contains no subgraph isomorphic to
     7003        `K_3`, an exception is raised in this case ::
     7004   
     7005             sage: g.subgraph_search(graphs.CompleteGraph(3))
     7006             Traceback (most recent call last):
     7007             ...
     7008             ValueError: No copy of the graph exists
     7009        """
     7010        from sage.graphs.generic_graph_pyx import subgraph_search
     7011
     7012        H = subgraph_search(self, G)
     7013
     7014        if H == []:
     7015            raise ValueError('No copy of the graph exists')
     7016       
     7017        return self.subgraph(H)
    69147018   
    69157019    def random_subgraph(self, p, inplace=False):
    69167020        """
  • sage/graphs/generic_graph_pyx.pyx

    diff -r decbd57f6680 -r 5a688fdc03a3 sage/graphs/generic_graph_pyx.pyx
    a b  
    44AUTHORS:
    55    -- Robert L. Miller   (2007-02-13): initial version
    66    -- Robert W. Bradshaw (2007-03-31): fast spring layout algorithms
     7    -- Nathann Cohen                  : exhaustive search
    78"""
    89
    910#*****************************************************************************
     
    1718include "../ext/interrupt.pxi"
    1819include '../ext/cdefs.pxi'           
    1920include '../ext/stdsage.pxi'
     21
     22from sage.graphs.base.dense_graph cimport DenseGraph
    2023from random import random
    2124
    2225cdef extern from *:
     
    450453    return m[:n*n]
    451454
    452455
     456# Exhaustive search in graphs
    453457
     458cpdef subgraph_search(G,H, induced = False):
     459    r"""
     460    Returns a set of vertices in G representing a copy of H
    454461
     462    ALGORITHM:
    455463
     464    This algorithm is a brute-force search.
     465    Let `V(H)=\{h_1,\dots,h_k\}`.  It first tries
     466    to find in `G` a possible representant of `h_1`, then a
     467    representant of `h_2` compatible with `h_1`, then
     468    a representant of `h_3` compatible with the first
     469    two, etc ...
    456470
     471    This way, most of the times we need to test far less than
     472    `\binom k!{|V(G)|}{k}` subsets, and hope this brute-force
     473    technique can sometimes be useful.
    457474
     475    INPUT:
    458476
     477    - ``G``, ``H`` -- graphs
    459478
     479    - ``induced`` (boolean) -- whether to require that the subgraph
     480      is an induced subgraph
    460481
     482    OUTPUT:
     483
     484    A list of vertices inducing a copy of ``H``. If none is found,
     485    an empty list is returned.
     486
     487    EXAMPLE:
     488
     489    A Petersen Graph contains an induced `P_5` ::
     490
     491        sage: from sage.graphs.generic_graph_pyx import subgraph_search
     492        sage: g = graphs.PetersenGraph()
     493        sage: subgraph_search(g, graphs.PathGraph(5), induced = True)
     494        [0, 1, 2, 3, 8]
     495
     496    It also contains a Claw (`K_{1,3}`)::
     497
     498        sage: subgraph_search(g, graphs.ClawGraph())
     499        [0, 1, 4, 5]
     500
     501    Though it contains no induced `P_6` ::
     502
     503        sage: subgraph_search(g, graphs.PathGraph(6), induced = True)
     504        []
     505    """
     506
     507    cdef int ng = G.order()
     508    cdef int nh = H.order()
     509    cdef int i, j, k
     510    cdef int * tmp_array
     511
     512    cdef (int) (*is_admissible) (int, int *, int *)
     513
     514    if induced:
     515        is_admissible = vectors_equal
     516    else:
     517        is_admissible = vectors_inferior
     518
     519    # Static copies of the two graphs for
     520    # more efficient operations
     521
     522    cdef DenseGraph g = DenseGraph(ng)
     523    cdef DenseGraph h = DenseGraph(nh)
     524
     525    # Copying the matrices
     526
     527    i = 0
     528    for l in G.adjacency_matrix():
     529        j = 0
     530        for k in l:
     531            if k:
     532                g.add_arc(i,j)
     533            j=j+1
     534        i=i+1
     535
     536    i = 0
     537    for l in H.adjacency_matrix():
     538        j = 0
     539        for k in l:
     540            if k:
     541                h.add_arc(i,j)
     542            j=j+1
     543        i=i+1
     544
     545    # A vertex is said to be busy if it is already part
     546    # of the partial copy of H in G
     547    cdef int * busy
     548    busy = <int *> sage_malloc(ng*sizeof(int))
     549    memset(busy, 0, ng*sizeof(int))
     550
     551    # 0 is the first vertex we use, so it is at first
     552    # busy
     553    busy[0] = 1
     554
     555    # stack
     556    #
     557    # List of the vertices which are part of the
     558    # partial copy of H in G
     559    #
     560    # stack[i] is the integer corresponding
     561    # to the vertex of G representing
     562    # the i th vertex of H
     563    #
     564    # stack[i] = -1 means that i is not represented
     565    # ... yet !
     566
     567    cdef int * stack
     568    stack = <int *> sage_malloc(nh*sizeof(int))
     569    stack[0] = 0
     570    stack[1] = -1
     571
     572    # number of representants we have already
     573    # found
     574
     575    # set to 1 as vertex 0 is already part of the partial
     576    # copy of H ...
     577    cdef int active = 1
     578
     579    # vertices is equal to range(nh), as an int * variable
     580    cdef int * vertices
     581    vertices = <int *> sage_malloc(nh*sizeof(int))
     582    for 0<= i < nh:
     583        vertices[i] = i
     584
     585    # line_h[i] represents the adjacency sequence of vertex i
     586    # in h relatively to vertices 0...i-1
     587    cdef int ** line_h
     588    line_h = <int **> sage_malloc(nh * sizeof(int *))
     589    for 0<= i < nh:
     590        line_h[i] = <int *> h.adjacency_sequence( i, vertices, i)
     591
     592    # the sequence of vertices to be returned
     593    value = []
     594   
     595    _sig_on
     596
     597    # as long as there is a non-void partial copy of H in G
     598    while active:
     599
     600        # If we are here and found nothing yet
     601        # we try the next possible vertex
     602        # as a representant of the active th
     603        # vertex of H
     604        i = stack[active] +1
     605
     606        # Looking for a vertex which is not busy
     607        # and compatible with the partial copy we have of H
     608        while i < ng:
     609            if busy[i] == 1:
     610                i = i + 1
     611            else:
     612                tmp_array =  g.adjacency_sequence(active, stack, i)
     613
     614                if is_admissible(active, tmp_array, line_h[active]):
     615                    free(tmp_array)
     616                    break
     617                else:
     618                    free(tmp_array)
     619                    i = i + 1
     620
     621        # if we found none, it means that we can not extend the current copy of H
     622        # so we update the status of stack[active]
     623        # and prepare to change the previous vertex
     624        if i >= ng:
     625            if stack[active] != -1:
     626                busy[stack[active]] = 0
     627            stack[active] = -1
     628            active=active-1
     629
     630
     631        # If we have found a good representant of H's i^{th} vertex in G
     632        else:
     633
     634            if stack[active] != -1:
     635                busy[stack[active]]=0
     636            stack[active] = i
     637            busy[stack[active]]=1
     638
     639            active = active + 1
     640           
     641            # We have found our copy !!!
     642            if active == nh:
     643                g_vertices = G.vertices()
     644                value = [g_vertices[stack[i]] for i in xrange(nh)]
     645                break
     646
     647            else:
     648                # we begin the search of the next vertex at 0
     649                stack[active] = -1
     650
     651    _sig_off
     652
     653    # Free the memory
     654    sage_free(busy)
     655    sage_free(stack)
     656    sage_free(vertices)
     657    for 0<= i < nh:
     658        sage_free(line_h[i])
     659    sage_free(line_h)
     660
     661    return value
     662
     663
     664cdef int vectors_equal(int n, int * a, int * b):
     665    r"""
     666    Tests whether two vectors given in argument are equal
     667
     668    INPUT:
     669
     670    - ``n`` -- length of the vectors
     671    - ``a``,``b`` -- the two vectors
     672    """
     673
     674    cdef int i =0
     675    for 0<= i < n:
     676        if a[i] != b[i]:
     677            return False
     678    return True
     679
     680cdef int vectors_inferior(int n, int * a, int * b):
     681    r"""
     682    Tests whether the second vector of integer is larger than the first
     683
     684    INPUT:
     685
     686    - ``n`` -- length of the vectors
     687    - ``a``,``b`` -- the two vectors
     688    """
     689
     690    cdef int i =0
     691    for 0<= i < n:
     692        if a[i] < b[i]:
     693            return False
     694    return True
     695
  • sage/graphs/graph.py

    diff -r decbd57f6680 -r 5a688fdc03a3 sage/graphs/graph.py
    a b  
    21132113            rs_dict[h] = [v for v in self if rs[h][v]==1]
    21142114
    21152115        return rs_dict
    2116            
     2116
    21172117
    21182118    ### Centrality
    21192119