Path: blob/master/src/sage/groups/perm_gps/partn_ref/refinement_graphs.pyx
8817 views
"""1Graph-theoretic partition backtrack functions23DOCTEST:4sage: import sage.groups.perm_gps.partn_ref.refinement_graphs56REFERENCE:78[1] McKay, Brendan D. Practical Graph Isomorphism. Congressus Numerantium,9Vol. 30 (1981), pp. 45-87.1011"""1213#*****************************************************************************14# Copyright (C) 2006 - 2011 Robert L. Miller <[email protected]>15#16# Distributed under the terms of the GNU General Public License (GPL)17# http://www.gnu.org/licenses/18#*****************************************************************************1920include 'data_structures_pyx.pxi' # includes bitsets2122def isomorphic(G1, G2, partn, ordering2, dig, use_indicator_function, sparse=False):23"""24Tests whether two graphs are isomorphic.2526sage: from sage.groups.perm_gps.partn_ref.refinement_graphs import isomorphic2728sage: G = Graph(2)29sage: H = Graph(2)30sage: isomorphic(G, H, [[0,1]], [0,1], 0, 1)31{0: 0, 1: 1}32sage: isomorphic(G, H, [[0,1]], [0,1], 0, 1)33{0: 0, 1: 1}34sage: isomorphic(G, H, [[0],[1]], [0,1], 0, 1)35{0: 0, 1: 1}36sage: isomorphic(G, H, [[0],[1]], [1,0], 0, 1)37{0: 1, 1: 0}3839sage: G = Graph(3)40sage: H = Graph(3)41sage: isomorphic(G, H, [[0,1,2]], [0,1,2], 0, 1)42{0: 0, 1: 1, 2: 2}43sage: G.add_edge(0,1)44sage: isomorphic(G, H, [[0,1,2]], [0,1,2], 0, 1)45False46sage: H.add_edge(1,2)47sage: isomorphic(G, H, [[0,1,2]], [0,1,2], 0, 1)48{0: 1, 1: 2, 2: 0}4950"""51cdef PartitionStack *part52cdef int *output, *ordering53cdef CGraph G54cdef GraphStruct GS1 = GraphStruct()55cdef GraphStruct GS2 = GraphStruct()56cdef GraphStruct GS57cdef int i, j, k, n = -1, cell_len58cdef list partition, cell59cdef bint loops = 06061from sage.graphs.all import Graph, DiGraph62from sage.graphs.generic_graph import GenericGraph63from copy import copy64which_G = 165for G_in in [G1, G2]:66if which_G == 1:67GS = GS168first=True69else:70GS = GS271first=False72if isinstance(G_in, GenericGraph):73if G_in.has_loops():74loops = 175if n == -1:76n = G_in.num_verts()77elif n != G_in.num_verts():78return False79if G_in.vertices() != range(n):80G_in = copy(G_in)81to = G_in.relabel(return_map=True)82frm = {}83for v in to.iterkeys():84frm[to[v]] = v85if first:86partition = [[to[v] for v in cell] for cell in partn]87else:88if first:89partition = partn90to = range(n)91frm = to92if sparse:93G = SparseGraph(n)94else:95G = DenseGraph(n)96if G_in.is_directed():97for i,j in G_in.edge_iterator(labels=False):98G.add_arc(i,j)99else:100for i,j in G_in.edge_iterator(labels=False):101G.add_arc(i,j)102G.add_arc(j,i)103elif isinstance(G_in, CGraph):104G = <CGraph> G_in105if n == -1:106n = G.num_verts107elif n != G.num_verts:108return False109if not loops:110for i from 0 <= i < n:111if G.has_arc_unsafe(i,i):112loops = 1113to = {}114for a in G.verts(): to[a]=a115frm = to116if first:117partition = partn118else:119raise TypeError("G must be a Sage graph.")120if first: frm1=frm;to1=to121else: frm2=frm;to2=to122GS.G = G123GS.directed = 1 if dig else 0124GS.loops = 1125GS.use_indicator = 1 if use_indicator_function else 0126which_G += 1127128if n == 0:129return {}130131part = PS_from_list(partition)132ordering = <int *> sage_malloc(n * sizeof(int))133output = <int *> sage_malloc(n * sizeof(int))134if part is NULL or ordering is NULL or output is NULL:135PS_dealloc(part)136sage_free(ordering)137sage_free(output)138raise MemoryError139for i from 0 <= i < n:140ordering[i] = to2[ordering2[i]]141142GS1.scratch = <int *> sage_malloc((3*n+1) * sizeof(int))143GS2.scratch = <int *> sage_malloc((3*n+1) * sizeof(int))144if GS1.scratch is NULL or GS2.scratch is NULL:145sage_free(GS1.scratch)146sage_free(GS2.scratch)147PS_dealloc(part)148sage_free(ordering)149raise MemoryError150151cdef bint isomorphic = double_coset(<void *>GS1, <void *>GS2, part, ordering, n, &all_children_are_equivalent, &refine_by_degree, &compare_graphs, NULL, NULL, output)152153PS_dealloc(part)154sage_free(ordering)155sage_free(GS1.scratch)156sage_free(GS2.scratch)157if isomorphic:158output_py = dict([[frm1[i], frm2[output[i]]] for i from 0 <= i < n])159else:160output_py = False161sage_free(output)162return output_py163164def search_tree(G_in, partition, lab=True, dig=False, dict_rep=False, certify=False,165verbosity=0, use_indicator_function=True, sparse=True,166base=False, order=False):167"""168Compute canonical labels and automorphism groups of graphs.169170INPUT:171G_in -- a Sage graph172partition -- a list of lists representing a partition of the vertices173lab -- if True, compute and return the canonical label in addition to the174automorphism group.175dig -- set to True for digraphs and graphs with loops. If True, does not176use optimizations based on Lemma 2.25 in [1] that are valid only for177simple graphs.178dict_rep -- if True, return a dictionary with keys the vertices of the179input graph G_in and values elements of the set the permutation group180acts on. (The point is that graphs are arbitrarily labelled, often1810..n-1, and permutation groups always act on 1..n. This dictionary182maps vertex labels (such as 0..n-1) to the domain of the permutations.)183certify -- if True, return the permutation from G to its canonical label.184verbosity -- currently ignored185use_indicator_function -- option to turn off indicator function186(True is generally faster)187sparse -- whether to use sparse or dense representation of the graph188(ignored if G is already a CGraph - see sage.graphs.base)189base -- whether to return the first sequence of split vertices (used in190computing the order of the group)191order -- whether to return the order of the automorphism group192193OUTPUT:194Depends on the options. If more than one thing is returned, they are in a195tuple in the following order:196list of generators in list-permutation format -- always197dict -- if dict_rep198graph -- if lab199dict -- if certify200list -- if base201integer -- if order202203DOCTEST:204sage: st = sage.groups.perm_gps.partn_ref.refinement_graphs.search_tree205sage: from sage.graphs.base.dense_graph import DenseGraph206sage: from sage.graphs.base.sparse_graph import SparseGraph207208Graphs on zero vertices:209sage: G = Graph()210sage: st(G, [[]], order=True)211([], Graph on 0 vertices, 1)212213Graphs on one vertex:214sage: G = Graph(1)215sage: st(G, [[0]], order=True)216([], Graph on 1 vertex, 1)217218Graphs on two vertices:219sage: G = Graph(2)220sage: st(G, [[0,1]], order=True)221([[1, 0]], Graph on 2 vertices, 2)222sage: st(G, [[0],[1]], order=True)223([], Graph on 2 vertices, 1)224sage: G.add_edge(0,1)225sage: st(G, [[0,1]], order=True)226([[1, 0]], Graph on 2 vertices, 2)227sage: st(G, [[0],[1]], order=True)228([], Graph on 2 vertices, 1)229230Graphs on three vertices:231sage: G = Graph(3)232sage: st(G, [[0,1,2]], order=True)233([[0, 2, 1], [1, 0, 2]], Graph on 3 vertices, 6)234sage: st(G, [[0],[1,2]], order=True)235([[0, 2, 1]], Graph on 3 vertices, 2)236sage: st(G, [[0],[1],[2]], order=True)237([], Graph on 3 vertices, 1)238sage: G.add_edge(0,1)239sage: st(G, [range(3)], order=True)240([[1, 0, 2]], Graph on 3 vertices, 2)241sage: st(G, [[0],[1,2]], order=True)242([], Graph on 3 vertices, 1)243sage: st(G, [[0,1],[2]], order=True)244([[1, 0, 2]], Graph on 3 vertices, 2)245246The Dodecahedron has automorphism group of size 120:247sage: G = graphs.DodecahedralGraph()248sage: Pi = [range(20)]249sage: st(G, Pi, order=True)[2]250120251252The three-cube has automorphism group of size 48:253sage: G = graphs.CubeGraph(3)254sage: G.relabel()255sage: Pi = [G.vertices()]256sage: st(G, Pi, order=True)[2]25748258259We obtain the same output using different types of Sage graphs:260sage: G = graphs.DodecahedralGraph()261sage: GD = DenseGraph(20)262sage: GS = SparseGraph(20)263sage: for i,j,_ in G.edge_iterator():264... GD.add_arc(i,j); GD.add_arc(j,i)265... GS.add_arc(i,j); GS.add_arc(j,i)266sage: Pi=[range(20)]267sage: a,b = st(G, Pi)268sage: asp,bsp = st(GS, Pi)269sage: ade,bde = st(GD, Pi)270sage: bsg = Graph()271sage: bdg = Graph()272sage: for i in range(20):273... for j in range(20):274... if bsp.has_arc(i,j):275... bsg.add_edge(i,j)276... if bde.has_arc(i,j):277... bdg.add_edge(i,j)278sage: print a, b.graph6_string()279[[0, 19, 3, 2, 6, 5, 4, 17, 18, 11, 10, 9, 13, 12, 16, 15, 14, 7, 8, 1], [0, 1, 8, 9, 13, 14, 7, 6, 2, 3, 19, 18, 17, 4, 5, 15, 16, 12, 11, 10], [1, 8, 9, 10, 11, 12, 13, 14, 7, 6, 2, 3, 4, 5, 15, 16, 17, 18, 19, 0]] S?[PG__OQ@?_?_?P?CO?_?AE?EC?Ac?@O280sage: a == asp281True282sage: a == ade283True284sage: b == bsg285True286sage: b == bdg287True288289Cubes!290sage: C = graphs.CubeGraph(1)291sage: gens, order = st(C, [C.vertices()], lab=False, order=True); order2922293sage: C = graphs.CubeGraph(2)294sage: gens, order = st(C, [C.vertices()], lab=False, order=True); order2958296sage: C = graphs.CubeGraph(3)297sage: gens, order = st(C, [C.vertices()], lab=False, order=True); order29848299sage: C = graphs.CubeGraph(4)300sage: gens, order = st(C, [C.vertices()], lab=False, order=True); order301384302sage: C = graphs.CubeGraph(5)303sage: gens, order = st(C, [C.vertices()], lab=False, order=True); order3043840305sage: C = graphs.CubeGraph(6)306sage: gens, order = st(C, [C.vertices()], lab=False, order=True); order30746080308309One can also turn off the indicator function (note- this will take longer)310sage: D1 = DiGraph({0:[2],2:[0],1:[1]}, loops=True)311sage: D2 = DiGraph({1:[2],2:[1],0:[0]}, loops=True)312sage: a,b = st(D1, [D1.vertices()], dig=True, use_indicator_function=False)313sage: c,d = st(D2, [D2.vertices()], dig=True, use_indicator_function=False)314sage: b==d315True316317This example is due to Chris Godsil:318sage: HS = graphs.HoffmanSingletonGraph()319sage: alqs = [Set(c) for c in (HS.complement()).cliques_maximum()]320sage: Y = Graph([alqs, lambda s,t: len(s.intersection(t))==0])321sage: Y0,Y1 = Y.connected_components_subgraphs()322sage: st(Y0, [Y0.vertices()])[1] == st(Y1, [Y1.vertices()])[1]323True324sage: st(Y0, [Y0.vertices()])[1] == st(HS, [HS.vertices()])[1]325True326sage: st(HS, [HS.vertices()])[1] == st(Y1, [Y1.vertices()])[1]327True328329Certain border cases need to be tested as well:330sage: G = Graph('Fll^G')331sage: a,b,c = st(G, [range(G.num_verts())], order=True); b332Graph on 7 vertices333sage: c33448335sage: G = Graph(21)336sage: st(G, [range(G.num_verts())], order=True)[2] == factorial(21)337True338339sage: G = Graph('^????????????????????{??N??@w??FaGa?PCO@CP?AGa?_QO?Q@G?CcA??cc????Bo????{????F_')340sage: perm = {3:15, 15:3}341sage: H = G.relabel(perm, inplace=False)342sage: st(G, [range(G.num_verts())])[1] == st(H, [range(H.num_verts())])[1]343True344345sage: st(Graph(':Dkw'), [range(5)], lab=False, dig=True)346[[4, 1, 2, 3, 0], [0, 2, 1, 3, 4]]347348"""349cdef CGraph G350cdef int i, j, n351cdef Integer I352cdef bint loops353cdef aut_gp_and_can_lab *output354cdef PartitionStack *part355from sage.graphs.all import Graph, DiGraph356from sage.graphs.generic_graph import GenericGraph357from copy import copy358if isinstance(G_in, GenericGraph):359loops = G_in.has_loops()360n = G_in.num_verts()361if G_in.vertices() != range(n):362G_in = copy(G_in)363to = G_in.relabel(return_map=True)364frm = {}365for v in to.iterkeys():366frm[to[v]] = v367partition = [[to[v] for v in cell] for cell in partition]368else:369to = dict(enumerate(range(n)))370frm = to371if sparse:372G = SparseGraph(n)373else:374G = DenseGraph(n)375if G_in.is_directed():376for i,j in G_in.edge_iterator(labels=False):377G.add_arc(i,j)378else:379for i,j in G_in.edge_iterator(labels=False):380G.add_arc(i,j)381G.add_arc(j,i)382elif isinstance(G_in, CGraph):383G = <CGraph> G_in384n = G.num_verts385loops = 0386for i from 0 <= i < n:387if G.has_arc_unsafe(i,i):388loops = 1389to = {}390for a in G.verts(): to[a]=a391frm = to392else:393raise TypeError("G must be a Sage graph.")394395cdef GraphStruct GS = GraphStruct()396GS.G = G397GS.directed = 1 if dig else 0398GS.loops = loops399GS.use_indicator = 1 if use_indicator_function else 0400401if n == 0:402return_tuple = [[]]403if dict_rep:404return_tuple.append({})405if lab:406if isinstance(G_in, GenericGraph):407G_C = copy(G_in)408else:409if isinstance(G, SparseGraph):410G_C = SparseGraph(n)411else:412G_C = DenseGraph(n)413return_tuple.append(G_C)414if certify:415return_tuple.append({})416if base:417return_tuple.append([])418if order:419return_tuple.append(Integer(1))420if len(return_tuple) == 1:421return return_tuple[0]422else:423return tuple(return_tuple)424425GS.scratch = <int *> sage_malloc( (3*G.num_verts + 1) * sizeof(int) )426part = PS_from_list(partition)427if GS.scratch is NULL or part is NULL:428PS_dealloc(part)429sage_free(GS.scratch)430raise MemoryError431432lab_new = lab or certify433output = get_aut_gp_and_can_lab(<void *>GS, part, G.num_verts, &all_children_are_equivalent, &refine_by_degree, &compare_graphs, lab, NULL, NULL, NULL)434sage_free( GS.scratch )435# prepare output436list_of_gens = []437for i from 0 <= i < output.num_gens:438list_of_gens.append([output.generators[j+i*G.num_verts] for j from 0 <= j < G.num_verts])439return_tuple = [list_of_gens]440if dict_rep:441ddd = {}442for v in frm.iterkeys():443ddd[frm[v]] = v if v != 0 else n444return_tuple.append(ddd)445if lab:446if isinstance(G_in, GenericGraph):447G_C = copy(G_in)448G_C.relabel([output.relabeling[i] for i from 0 <= i < n])449else:450if isinstance(G, SparseGraph):451G_C = SparseGraph(n)452else:453G_C = DenseGraph(n)454for i from 0 <= i < n:455for j in G.out_neighbors(i):456G_C.add_arc(output.relabeling[i],output.relabeling[j])457return_tuple.append(G_C)458if certify:459dd = {}460for i from 0 <= i < G.num_verts:461dd[frm[i]] = output.relabeling[i]462return_tuple.append(dd)463if base:464return_tuple.append([output.group.base_orbits[i][0] for i from 0 <= i < output.group.base_size])465if order:466I = Integer()467SC_order(output.group, 0, I.value)468return_tuple.append(I)469PS_dealloc(part)470deallocate_agcl_output(output)471if len(return_tuple) == 1:472return return_tuple[0]473else:474return tuple(return_tuple)475476cdef int refine_by_degree(PartitionStack *PS, void *S, int *cells_to_refine_by, int ctrb_len):477r"""478Refines the input partition by checking degrees of vertices to the given479cells.480481INPUT:482PS -- a partition stack, whose finest partition is the partition to be483refined.484S -- a graph struct object, which contains scratch space, the graph in485question, and some flags.486cells_to_refine_by -- a list of pointers to cells to check degrees against487in refining the other cells (updated in place). Must be allocated to488length at least the degree of PS, since the array may grow489ctrb_len -- how many cells in cells_to_refine_by490491OUTPUT:492493An integer invariant under the orbits of $S_n$. That is, if $\gamma$ is a494permutation of the vertices, then495$$ I(G, PS, cells_to_refine_by) = I( \gamma(G), \gamma(PS), \gamma(cells_to_refine_by) ) .$$496497"""498cdef GraphStruct GS = <GraphStruct> S499cdef CGraph G = GS.G500cdef int current_cell_against = 0501cdef int current_cell, i, r502cdef int first_largest_subcell503cdef int invariant = 1504cdef int max_degree505cdef int *degrees = GS.scratch # length 3n+1506cdef bint necessary_to_split_cell507cdef int against_index508if G.num_verts != PS.degree and PS.depth == 0:509# should be less verts, then, so place the "nonverts" in separate cell at the end510current_cell = 0511while current_cell < PS.degree:512i = current_cell513r = 0514while 1:515if G.has_vertex(PS.entries[i]):516degrees[i-current_cell] = 0517else:518r = 1519degrees[i-current_cell] = 1520i += 1521if PS.levels[i-1] <= PS.depth:522break523if r != 0:524sort_by_function(PS, current_cell, degrees)525current_cell = i526while not PS_is_discrete(PS) and current_cell_against < ctrb_len:527invariant += 1528current_cell = 0529while current_cell < PS.degree:530invariant += 50531i = current_cell532necessary_to_split_cell = 0533max_degree = 0534while 1:535degrees[i-current_cell] = degree(PS, G, i, cells_to_refine_by[current_cell_against], 0)536if degrees[i-current_cell] != degrees[0]:537necessary_to_split_cell = 1538if degrees[i-current_cell] > max_degree:539max_degree = degrees[i-current_cell]540i += 1541if PS.levels[i-1] <= PS.depth:542break543# now, i points to the next cell (before refinement)544if necessary_to_split_cell:545invariant += 10546first_largest_subcell = sort_by_function(PS, current_cell, degrees)547invariant += first_largest_subcell + max_degree548against_index = current_cell_against549while against_index < ctrb_len:550if cells_to_refine_by[against_index] == current_cell:551cells_to_refine_by[against_index] = first_largest_subcell552break553against_index += 1554r = current_cell555while 1:556if r == current_cell or PS.levels[r-1] == PS.depth:557if r != first_largest_subcell:558cells_to_refine_by[ctrb_len] = r559ctrb_len += 1560r += 1561if r >= i:562break563invariant += (i - current_cell)564current_cell = i565if GS.directed:566# if we are looking at a digraph, also compute567# the reverse degrees and sort by them568current_cell = 0569while current_cell < PS.degree: # current_cell is still a valid cell570invariant += 20571i = current_cell572necessary_to_split_cell = 0573max_degree = 0574while 1:575degrees[i-current_cell] = degree(PS, G, i, cells_to_refine_by[current_cell_against], 1)576if degrees[i-current_cell] != degrees[0]:577necessary_to_split_cell = 1578if degrees[i-current_cell] > max_degree:579max_degree = degrees[i-current_cell]580i += 1581if PS.levels[i-1] <= PS.depth:582break583# now, i points to the next cell (before refinement)584if necessary_to_split_cell:585invariant += 7586first_largest_subcell = sort_by_function(PS, current_cell, degrees)587invariant += first_largest_subcell + max_degree588against_index = current_cell_against589while against_index < ctrb_len:590if cells_to_refine_by[against_index] == current_cell:591cells_to_refine_by[against_index] = first_largest_subcell592break593against_index += 1594against_index = ctrb_len595r = current_cell596while 1:597if r == current_cell or PS.levels[r-1] == PS.depth:598if r != first_largest_subcell:599cells_to_refine_by[against_index] = r600against_index += 1601ctrb_len += 1602r += 1603if r >= i:604break605invariant += (i - current_cell)606current_cell = i607current_cell_against += 1608if GS.use_indicator:609return invariant610else:611return 0612613cdef int compare_graphs(int *gamma_1, int *gamma_2, void *S1, void *S2, int degree):614r"""615Compare gamma_1(S1) and gamma_2(S2).616617Return return -1 if gamma_1(S1) < gamma_2(S2), 0 if gamma_1(S1) ==618gamma_2(S2), 1 if gamma_1(S1) > gamma_2(S2). (Just like the python619\code{cmp}) function.620621INPUT:622gamma_1, gamma_2 -- list permutations (inverse)623S1, S2 -- graph struct objects624625"""626cdef int i, j, m627cdef GraphStruct GS1 = <GraphStruct> S1628cdef GraphStruct GS2 = <GraphStruct> S2629cdef CGraph G1 = GS1.G630cdef CGraph G2 = GS2.G631if G1.active_vertices.size != G2.active_vertices.size or \632not bitset_cmp(G1.active_vertices, G2.active_vertices):633for i from 0 <= i < degree:634if G1.has_vertex(gamma_1[i]) != G2.has_vertex(gamma_2[i]):635return G1.has_vertex(gamma_1[i]) - G2.has_vertex(gamma_2[i])636for i from 0 <= i < G1.num_verts:637for j from 0 <= j < G1.num_verts:638if G1.has_arc_unsafe(gamma_1[i], gamma_1[j]):639if not G2.has_arc_unsafe(gamma_2[i], gamma_2[j]):640return 1641elif G2.has_arc_unsafe(gamma_2[i], gamma_2[j]):642return -1643return 0644645cdef bint all_children_are_equivalent(PartitionStack *PS, void *S):646"""647Return True if every refinement of the current partition results in the648same structure.649650WARNING:651Converse does not hold in general! See Lemma 2.25 of [1] for details.652653INPUT:654PS -- the partition stack to be checked655S -- a graph struct object656"""657cdef GraphStruct GS = <GraphStruct> S658if GS.directed or GS.loops:659return 0660cdef CGraph G = GS.G661cdef int i, n = PS.degree662cdef bint in_cell = 0663cdef int nontrivial_cells = 0664cdef int total_cells = PS_num_cells(PS)665if n <= total_cells + 4:666return 1667for i from 0 <= i < n-1:668if PS.levels[i] <= PS.depth:669if in_cell:670nontrivial_cells += 1671in_cell = 0672else:673in_cell = 1674if in_cell:675nontrivial_cells += 1676if n == total_cells + nontrivial_cells:677return 1678if n == total_cells + nontrivial_cells + 1:679return 1680return 0681682cdef inline int degree(PartitionStack *PS, CGraph G, int entry, int cell_index, bint reverse):683"""684Returns the number of edges from the vertex corresponding to entry to685vertices in the cell corresponding to cell_index.686687INPUT:688PS -- the partition stack to be checked689S -- a graph struct object690entry -- the position of the vertex in question in the entries of PS691cell_index -- the starting position of the cell in question in the entries692of PS693reverse -- whether to check for arcs in the other direction694"""695cdef int num_arcs = 0696entry = PS.entries[entry]697if not reverse:698while 1:699if G.has_arc_unsafe(PS.entries[cell_index], entry):700num_arcs += 1701if PS.levels[cell_index] > PS.depth:702cell_index += 1703else:704break705else:706while 1:707if G.has_arc_unsafe(entry, PS.entries[cell_index]):708num_arcs += 1709if PS.levels[cell_index] > PS.depth:710cell_index += 1711else:712break713return num_arcs714715def all_labeled_graphs(n):716"""717Returns all labeled graphs on n vertices {0,1,...,n-1}. Used in718classifying isomorphism types (naive approach), and more importantly719in benchmarking the search algorithm.720721EXAMPLE::722723sage: from sage.groups.perm_gps.partn_ref.refinement_graphs import all_labeled_graphs724sage: st = sage.groups.perm_gps.partn_ref.refinement_graphs.search_tree725sage: Glist = {}726sage: Giso = {}727sage: for n in [1..5]: # long time (4s on sage.math, 2011)728... Glist[n] = all_labeled_graphs(n)729... Giso[n] = []730... for g in Glist[n]:731... a, b = st(g, [range(n)])732... inn = False733... for gi in Giso[n]:734... if b == gi:735... inn = True736... if not inn:737... Giso[n].append(b)738sage: for n in Giso: # long time739... print n, len(Giso[n])7401 17412 27423 47434 117445 34745746"""747from sage.graphs.all import Graph748TE = []749for i in range(n):750for j in range(i):751TE.append((i, j))752m = len(TE)753Glist= []754for i in range(2**m):755G = Graph(n)756b = Integer(i).binary()757b = '0'*(m-len(b)) + b758for i in range(m):759if int(b[i]):760G.add_edge(TE[i])761Glist.append(G)762return Glist763764765def random_tests(num=10, n_max=60, perms_per_graph=5):766"""767Tests to make sure that C(gamma(G)) == C(G) for random permutations gamma768and random graphs G, and that isomorphic returns an isomorphism.769770INPUT:771num -- run tests for this many graphs772n_max -- test graphs with at most this many vertices773perms_per_graph -- test each graph with this many random permutations774775DISCUSSION:776777This code generates num random graphs G on at most n_max vertices. The778density of edges is chosen randomly between 0 and 1.779780For each graph G generated, we uniformly generate perms_per_graph random781permutations and verify that the canonical labels of G and the image of G782under the generated permutation are equal, and that the isomorphic function783returns an isomorphism.784785TESTS::786787sage: import sage.groups.perm_gps.partn_ref.refinement_graphs788sage: sage.groups.perm_gps.partn_ref.refinement_graphs.random_tests() # long time789All passed: 200 random tests on 20 graphs.790"""791from sage.misc.prandom import random, randint792from sage.graphs.graph_generators import GraphGenerators793from sage.graphs.digraph_generators import DiGraphGenerators794from sage.combinat.permutation import Permutations795from copy import copy796cdef int i, j, num_tests = 0, num_graphs = 0797GG = GraphGenerators()798DGG = DiGraphGenerators()799for mmm in range(num):800p = random()801n = randint(1, n_max)802S = Permutations(n)803804G = GG.RandomGNP(n, p)805H = copy(G)806for i from 0 <= i < perms_per_graph:807G = copy(H)808G1 = search_tree(G, [G.vertices()])[1]809perm = list(S.random_element())810perm = [perm[j]-1 for j from 0 <= j < n]811G.relabel(perm)812G2 = search_tree(G, [G.vertices()])[1]813if G1 != G2:814print "search_tree FAILURE: graph6-"815print H.graph6_string()816print perm817return818isom = isomorphic(G, H, [range(n)], range(n), 0, 1)819if not isom or G.relabel(isom, inplace=False) != H:820print "isom FAILURE: graph6-"821print H.graph6_string()822print perm823return824825D = DGG.RandomDirectedGNP(n, p)826D.allow_loops(True)827for i from 0 <= i < n:828if random() < p:829D.add_edge(i,i)830E = copy(D)831for i from 0 <= i < perms_per_graph:832D = copy(E)833D1 = search_tree(D, [D.vertices()], dig=True)[1]834perm = list(S.random_element())835perm = [perm[j]-1 for j from 0 <= j < n]836D.relabel(perm)837D2 = search_tree(D, [D.vertices()], dig=True)[1]838if D1 != D2:839print "search_tree FAILURE: dig6-"840print E.dig6_string()841print perm842return843isom = isomorphic(D, E, [range(n)], range(n), 1, 1)844if not isom or D.relabel(isom, inplace=False) != E:845print "isom FAILURE: dig6-"846print E.dig6_string()847print perm848print isom849return850num_tests += 4*perms_per_graph851num_graphs += 2852print "All passed: %d random tests on %d graphs."%(num_tests, num_graphs)853854def orbit_partition(gamma, list_perm=False):855r"""856Assuming that G is a graph on vertices 0,1,...,n-1, and gamma is an857element of SymmetricGroup(n), returns the partition of the vertex858set determined by the orbits of gamma, considered as action on the859set 1,2,...,n where we take 0 = n. In other words, returns the860partition determined by a cyclic representation of gamma.861862INPUT:863864865- ``list_perm`` - if True, assumes866``gamma`` is a list representing the map867`i \mapsto ``gamma``[i]`.868869870EXAMPLES::871872sage: from sage.groups.perm_gps.partn_ref.refinement_graphs import orbit_partition873sage: G = graphs.PetersenGraph()874sage: S = SymmetricGroup(10)875sage: gamma = S('(10,1,2,3,4)(5,6,7)(8,9)')876sage: orbit_partition(gamma)877[[1, 2, 3, 4, 0], [5, 6, 7], [8, 9]]878sage: gamma = S('(10,5)(1,6)(2,7)(3,8)(4,9)')879sage: orbit_partition(gamma)880[[1, 6], [2, 7], [3, 8], [4, 9], [5, 0]]881"""882if list_perm:883n = len(gamma)884seen = [1] + [0]*(n-1)885i = 0886p = 0887partition = [[0]]888while sum(seen) < n:889if gamma[i] != partition[p][0]:890partition[p].append(gamma[i])891i = gamma[i]892seen[i] = 1893else:894for j in range(n):895if seen[j]==0:896i = j897break898partition.append([i])899p += 1900seen[i] = 1901return partition902else:903n = len(gamma.domain())904l = []905for i in range(1,n+1):906orb = gamma.orbit(i)907if orb not in l: l.append(orb)908for i in l:909for j in range(len(i)):910if i[j] == n:911i[j] = 0912return l913914def coarsest_equitable_refinement(CGraph G, list partition, bint directed):915"""916Returns the coarsest equitable refinement of ``partition`` for ``G``.917918This is a helper function for the graph function of the same name.919920DOCTEST (More thorough testing in ``sage/graphs/graph.py``)::921922sage: from sage.groups.perm_gps.partn_ref.refinement_graphs import coarsest_equitable_refinement923sage: from sage.graphs.base.sparse_graph import SparseGraph924sage: coarsest_equitable_refinement(SparseGraph(7), [[0], [1,2,3,4], [5,6]], 0)925[[0], [1, 2, 3, 4], [5, 6]]926927"""928cdef int i, j = 0, k = 0, n = G.num_verts929930# set up partition stack and graph struct931cdef PartitionStack *nu = PS_new(n, 0)932for cell in partition:933for i in cell:934nu.entries[j] = i935nu.levels[j] = n936j += 1937nu.levels[j-1] = 0938PS_move_min_to_front(nu, k, j-1)939k = j940941cdef GraphStruct GS = GraphStruct()942GS.G = G943GS.scratch = <int *> sage_malloc((3*n+1) * sizeof(int))944if GS.scratch is NULL:945PS_dealloc(nu)946raise MemoryError947GS.directed = directed948GS.use_indicator = 0949950# set up cells to refine by951cdef int num_cells = len(partition)952cdef int *alpha = <int *>sage_malloc(n * sizeof(int))953if alpha is NULL:954PS_dealloc(nu)955sage_free(GS.scratch)956raise MemoryError957j = 0958for i from 0 <= i < num_cells:959alpha[i] = j960j += len(partition[i])961962# refine, and get the result963refine_by_degree(nu, <void *>GS, alpha, num_cells)964965eq_part = []966cell = []967for i from 0 <= i < n:968cell.append(nu.entries[i])969if nu.levels[i] <= 0:970eq_part.append(cell)971cell = []972973PS_dealloc(nu)974sage_free(GS.scratch)975sage_free(alpha)976977return eq_part978979def get_orbits(list gens, int n):980"""981Compute orbits given a list of generators of a permutation group, in list982format.983984This is a helper function for automorphism groups of graphs.985986DOCTEST (More thorough testing in ``sage/graphs/graph.py``)::987988sage: from sage.groups.perm_gps.partn_ref.refinement_graphs import get_orbits989sage: get_orbits([[1,2,3,0,4,5], [0,1,2,3,5,4]], 6)990[[0, 1, 2, 3], [4, 5]]991992"""993cdef int i, j994if len(gens) == 0:995return [[i] for i from 0 <= i < n]996997cdef OrbitPartition *OP = OP_new(n)998cdef int *perm_ints = <int *> sage_malloc(n * sizeof(int))999if perm_ints is NULL:1000OP_dealloc(OP)1001raise MemoryError10021003for gen in gens:1004for i from 0 <= i < n:1005perm_ints[i] = gen[i]1006OP_merge_list_perm(OP, perm_ints)10071008orbit_dict = {}1009for i from 0 <= i < n:1010j = OP_find(OP, i)1011if orbit_dict.has_key(j):1012orbit_dict[j].append(i)1013else:1014orbit_dict[j] = [i]10151016OP_dealloc(OP)1017sage_free(perm_ints)10181019return orbit_dict.values()10201021102210231024102510261027# Canonical augmentation1028from cpython.ref cimport *10291030# Dense graphs: adding edges10311032# This implements an augmentation scheme as follows:1033# * Seed objects are graphs with n vertices and no edges.1034# * Augmentations consist of adding a single edge, or a loop.10351036cdef void *dg_edge_gen_next(void *data, int *degree, bint *mem_err):1037r"""1038The ``next`` function in an edge iterator. The iterator generates unique1039representatives under the action of the automorphism group of the parent1040graph on edges not in the graph, which are to considered for adding to the1041graph.1042"""1043cdef dg_edge_gen_data *degd = <dg_edge_gen_data *> data1044cdef GraphStruct graph = <GraphStruct> degd.graph1045cdef subset *edge_candidate1046cdef int u, v, reject1047cdef bint mem_err_sub = 01048if mem_err[0]:1049(<canonical_generator_data *> degd.edge_iterator.data).mem_err = 11050while 1:1051edge_candidate = <subset *> degd.edge_iterator.next(degd.edge_iterator.data, NULL, &mem_err_sub)1052if edge_candidate is NULL:1053break1054reject = 01055if bitset_len(&edge_candidate.bits) < (1 if graph.loops else 2):1056reject = 11057else:1058u = bitset_first(&edge_candidate.bits)1059v = bitset_next(&edge_candidate.bits, u+1)1060if v == -1: v = u1061if graph.G.has_arc_unsafe(u, v):1062reject = 11063if not reject:1064break1065if mem_err_sub:1066mem_err[0] = 11067return edge_candidate10681069cdef void *allocate_degd(int degree):1070r"""1071Allocate the data part of the iterator over edges to add to the graph.1072"""1073cdef dg_edge_gen_data *degd = <dg_edge_gen_data *> sage_malloc(sizeof(dg_edge_gen_data))1074cdef iterator *edge_iterator = allocate_subset_gen(degree, 2)1075if degd is NULL or edge_iterator is NULL:1076sage_free(degd)1077free_subset_gen(edge_iterator)1078return NULL1079edge_iterator = setup_set_gen(edge_iterator, degree, 2)1080if edge_iterator is NULL:1081sage_free(degd)1082return NULL1083degd.edge_iterator = edge_iterator1084return degd10851086cdef void deallocate_degd(void *data):1087r"""1088Deallocate the data part of the iterator over edges to add to the graph.1089"""1090cdef dg_edge_gen_data *degd = <dg_edge_gen_data *> data1091free_subset_gen(degd.edge_iterator)1092sage_free(degd)10931094cdef int gen_children_dg_edge(void *S, aut_gp_and_can_lab *group, iterator *it):1095r"""1096Setup an iterator over edges to be added.1097"""1098cdef GraphStruct GS = <GraphStruct> S1099cdef int n = GS.G.num_verts1100(<dg_edge_gen_data *> it.data).graph = <void *> GS1101cdef iterator *edge_iterator = setup_set_gen((<dg_edge_gen_data *> it.data).edge_iterator, n, 2)1102if edge_iterator is not NULL:1103start_canonical_generator(group.group, NULL, n, edge_iterator)1104return (edge_iterator is NULL)11051106cdef void copy_dense_graph(DenseGraph dest, DenseGraph src):1107r"""1108caution! active_vertices must be same size!1109"""1110memcpy(dest.edges, src.edges, src.active_vertices.size * src.num_longs * sizeof(unsigned long))1111memcpy(dest.in_degrees, src.in_degrees, src.active_vertices.size * sizeof(int))1112memcpy(dest.out_degrees, src.out_degrees, src.active_vertices.size * sizeof(int))1113bitset_copy(dest.active_vertices, src.active_vertices)1114dest.num_verts = src.num_verts1115dest.num_arcs = src.num_arcs11161117cdef void *apply_dg_edge_aug(void *parent, void *aug, void *child, int *degree, bint *mem_err):1118r"""1119Apply the augmentation to ``parent`` storing the result in ``child``. Here1120``aug`` represents an edge to be added.1121"""1122cdef GraphStruct GS_child = <GraphStruct> child, GS_par = <GraphStruct> parent1123cdef DenseGraph DG = <DenseGraph> GS_child.G, DG_par = <DenseGraph> GS_par.G1124cdef subset *edge = <subset *> aug1125cdef int u, v, n = DG_par.num_verts11261127# copy DG_par edges to DG1128copy_dense_graph(DG, DG_par)11291130# add the edge1131u = bitset_first(&edge.bits)1132v = bitset_next(&edge.bits, u+1)1133if v == -1:1134DG.add_arc_unsafe(u, u)1135else:1136DG.add_arc_unsafe(u, v)1137DG.add_arc_unsafe(v, u)11381139degree[0] = DG.num_verts1140return <void *> GS_child11411142cdef void *allocate_dg_edge(int n, bint loops):1143r"""1144Allocates an object for this augmentation scheme.1145"""1146cdef GraphStruct GS1147cdef DenseGraph G1148cdef int *scratch1149try:1150GS = GraphStruct()1151G = DenseGraph(n)1152scratch = <int *> sage_malloc((3*n+1) * sizeof(int))1153if scratch is NULL:1154raise MemoryError1155except MemoryError:1156return NULL1157Py_INCREF(GS)1158Py_INCREF(G)1159GS.G = G1160GS.directed = 01161GS.loops = loops1162GS.use_indicator = 11163GS.scratch = scratch1164return <void *> GS11651166cdef void free_dg_edge(void *child):1167r"""1168Deallocates an object for this augmentation scheme.1169"""1170cdef GraphStruct GS = <GraphStruct> child1171sage_free(GS.scratch)1172Py_DECREF(GS.G)1173Py_DECREF(GS)11741175cdef void *canonical_dg_edge_parent(void *child, void *parent, int *permutation, int *degree, bint *mem_err):1176r"""1177Applies ``permutation`` to ``child``, determines an arbitrary parent by1178deleting the lexicographically largest edge, applies the inverse of1179``permutation`` to the result and stores the result in ``parent``.1180"""1181cdef GraphStruct GS_par = <GraphStruct> parent, GS = <GraphStruct> child1182cdef DenseGraph DG_par = <DenseGraph> GS_par.G, DG = <DenseGraph> GS.G1183cdef int u, v, n = DG.num_verts1184cdef int *scratch = GS_par.scratch11851186# copy DG edges to DG_par1187copy_dense_graph(DG_par, DG)11881189# remove the right edge1190for u from 0 <= u < n:1191scratch[permutation[u]] = u1192for u from n > u >= 0:1193if DG.in_degrees[scratch[u]] != 0:1194break1195for v from u >= v >= 0:1196if DG.has_arc_unsafe(scratch[u], scratch[v]):1197break1198DG_par.del_arc_unsafe(scratch[u], scratch[v])1199if u != v:1200DG_par.del_arc_unsafe(scratch[v], scratch[u])12011202degree[0] = n1203return <void *> GS_par12041205cdef iterator *allocate_dg_edge_gen(int degree, int depth, bint loops):1206r"""1207Allocates the iterator for generating graphs.1208"""1209cdef iterator *dg_edge_gen = <iterator *> sage_malloc(sizeof(iterator))1210cdef canonical_generator_data *cgd = allocate_cgd(depth, degree)1211if dg_edge_gen is NULL or cgd is NULL:1212sage_free(dg_edge_gen)1213deallocate_cgd(cgd)1214return NULL1215cdef int i, j1216for i from 0 <= i < depth:1217cgd.object_stack[i] = allocate_dg_edge(degree, loops)1218cgd.parent_stack[i] = allocate_dg_edge(degree, loops)1219cgd.iterator_stack[i].data = allocate_degd(degree)1220cgd.iterator_stack[i].next = &dg_edge_gen_next1221if cgd.iterator_stack[i].data is NULL or \1222cgd.object_stack[i] is NULL or \1223cgd.parent_stack[i] is NULL:1224for j from 0 <= j <= i:1225deallocate_degd(cgd.iterator_stack[j].data)1226free_dg_edge(cgd.object_stack[j])1227free_dg_edge(cgd.parent_stack[j])1228sage_free(dg_edge_gen)1229deallocate_cgd(cgd)1230return NULL1231dg_edge_gen.data = <void *> cgd1232dg_edge_gen.next = &canonical_generator_next1233return dg_edge_gen12341235cdef void free_dg_edge_gen(iterator *dg_edge_gen):1236r"""1237Deallocates the iterator for generating graphs.1238"""1239cdef canonical_generator_data *cgd = <canonical_generator_data *> dg_edge_gen.data1240deallocate_cgd(cgd)1241sage_free(dg_edge_gen)124212431244def generate_dense_graphs_edge_addition(int n, bint loops, G = None, depth = None, bint construct = False,1245bint indicate_mem_err = True):1246r"""12471248EXAMPLES::12491250sage: from sage.groups.perm_gps.partn_ref.refinement_graphs import generate_dense_graphs_edge_addition12511252::12531254sage: for n in [0..6]:1255... print generate_dense_graphs_edge_addition(n,1)12561125721258612592012609012615441262509612631264::12651266sage: for n in [0..7]:1267... print generate_dense_graphs_edge_addition(n,0)126811269112702127141272111273341274156127510441276sage: generate_dense_graphs_edge_addition(8,0) # long time - about 14 seconds at 2.4 GHz12771234612781279"""1280from sage.graphs.all import Graph1281cdef iterator *graph_iterator1282cdef DenseGraph DG, ODG1283cdef GraphStruct GS1284if n < 0:1285return [] if construct else Integer(0)1286if n == 0:1287return [Graph(0, implementation='c_graph', sparse=False, loops=loops)] if construct else Integer(1)1288if n == 1:1289if not loops:1290return [Graph(1, implementation='c_graph', sparse=False, loops=loops)] if construct else Integer(1)1291else:1292if construct:1293G = Graph(1, implementation='c_graph', sparse=False, loops=loops)1294(<CGraph>G._backend._cg).add_arc_unsafe(0,0)1295return [G, Graph(1, implementation='c_graph', sparse=False, loops=loops)]1296else:1297return Integer(2)12981299if depth is None:1300depth = n*n13011302graph_iterator = allocate_dg_edge_gen(n, depth, loops)1303if graph_iterator is NULL:1304raise MemoryError13051306GS = (<GraphStruct> (<canonical_generator_data *> graph_iterator.data).object_stack[0])1307if G is not None:1308DG = GS.G1309for u,v in G.edges(labels=False):1310DG.add_arc(u,v)1311if u != v:1312DG.add_arc(v,u)13131314graph_iterator = setup_canonical_generator(n,1315&all_children_are_equivalent,1316&refine_by_degree,1317&compare_graphs,1318&gen_children_dg_edge,1319&apply_dg_edge_aug,1320&free_dg_edge,1321&deallocate_degd,1322&free_subset,1323&canonical_dg_edge_parent,1324depth, 0, graph_iterator)13251326start_canonical_generator(NULL, <void *> GS, n, graph_iterator)13271328cdef list out_list1329cdef void *thing1330cdef GraphStruct thing_gs1331cdef Integer number1332cdef bint mem_err = 01333if construct:1334out_list = []1335else:1336number = Integer(0)1337if construct:1338while 1:1339thing = graph_iterator.next(graph_iterator.data, NULL, &mem_err)1340if thing is NULL: break1341ODG = (<GraphStruct>thing).G1342G = Graph(0, implementation='c_graph', sparse=False)1343DG = DenseGraph(ODG.active_vertices.size, extra_vertices=0)1344copy_dense_graph(DG, ODG)1345G._backend._cg = DG1346out_list.append(G)1347else:1348while 1:1349thing = graph_iterator.next(graph_iterator.data, NULL, &mem_err)1350if thing is NULL: break1351number += 113521353free_dg_edge_gen(graph_iterator)1354if mem_err:1355if indicate_mem_err:1356raise MemoryError1357else:1358out_list.append(MemoryError())1359if construct:1360return out_list1361else:1362return number1363136413651366# Dense graphs: adding vertices13671368# This implements an augmentation scheme as follows:1369# * Seed objects are graphs with one verticex and no edges.1370# * Augmentations consist of adding a single vertex connected to some subset of1371# the previous vertices.13721373cdef int gen_children_dg_vert(void *S, aut_gp_and_can_lab *group, iterator *it):1374r"""1375Setup an iterator over subsets to join a new vertex to.1376"""1377cdef GraphStruct GS = <GraphStruct> S1378cdef int n = GS.G.num_verts1379cdef iterator *subset_iterator = setup_set_gen(it, n, n)1380if subset_iterator is not NULL:1381start_canonical_generator(group.group, NULL, n, subset_iterator)1382return (subset_iterator is NULL)13831384cdef void *apply_dg_vert_aug(void *parent, void *aug, void *child, int *degree, bint *mem_err):1385r"""1386Apply the augmentation to ``parent`` storing the result in ``child``. Here1387``aug`` represents a subset to join to a new vertex.1388"""1389cdef GraphStruct GS_child = <GraphStruct> child, GS_par = <GraphStruct> parent1390cdef DenseGraph DG = <DenseGraph> GS_child.G, DG_par = <DenseGraph> GS_par.G1391cdef subset *set1 = <subset *> aug1392cdef int u, n = DG_par.num_verts13931394# copy DG_par edges to DG1395copy_dense_graph(DG, DG_par)1396DG.add_vertex_unsafe(n)13971398# add the edges1399u = bitset_first(&set1.bits)1400while u != -1:1401DG.add_arc_unsafe(u, n)1402DG.add_arc_unsafe(n, u)1403u = bitset_next(&set1.bits, u+1)14041405degree[0] = n+11406return <void *> GS_child14071408cdef void *allocate_dg_vert(int n, int depth):1409r"""1410Allocates an object for this augmentation scheme.1411"""1412cdef GraphStruct GS1413cdef DenseGraph G1414cdef int *scratch1415try:1416GS = GraphStruct()1417G = DenseGraph(0, extra_vertices=depth)1418bitset_set_first_n(G.active_vertices, n)1419G.num_verts = n1420scratch = <int *> sage_malloc((3*depth+1) * sizeof(int))1421if scratch is NULL:1422raise MemoryError1423except MemoryError:1424return NULL1425Py_INCREF(GS)1426Py_INCREF(G)1427GS.G = G1428GS.directed = 01429GS.loops = 01430GS.use_indicator = 11431GS.scratch = scratch1432return <void *> GS14331434cdef void free_dg_vert(void *child):1435r"""1436Deallocates an object for this augmentation scheme.1437"""1438cdef GraphStruct GS = <GraphStruct> child1439sage_free(GS.scratch)1440Py_DECREF(GS.G)1441Py_DECREF(GS)14421443cdef void *canonical_dg_vert_parent(void *child, void *parent, int *permutation, int *degree, bint *mem_err):1444r"""1445Applies ``permutation`` to ``child``, determines an arbitrary parent by1446deleting the lexicographically largest vertex, applies the inverse of1447``permutation`` to the result and stores the result in ``parent``.1448"""1449cdef GraphStruct GS_par = <GraphStruct> parent, GS = <GraphStruct> child1450cdef DenseGraph DG_par = <DenseGraph> GS_par.G, DG = <DenseGraph> GS.G1451cdef int u, v, n = DG_par.num_verts1452cdef int *scratch = GS.scratch14531454# copy DG edges to DG_par1455copy_dense_graph(DG_par, DG)14561457# remove the right vertex1458for u from 0 <= u <= n:1459scratch[permutation[u]] = u1460DG_par.del_vertex_unsafe(scratch[n])14611462degree[0] = n1463return <void *> GS_par14641465cdef iterator *allocate_dg_vert_gen(int degree, int depth):1466r"""1467Allocates the iterator for generating graphs.1468"""1469cdef iterator *dg_vert_gen = <iterator *> sage_malloc(sizeof(iterator))1470cdef canonical_generator_data *cgd = allocate_cgd(depth, degree), *cgd21471if dg_vert_gen is NULL or cgd is NULL:1472sage_free(dg_vert_gen)1473deallocate_cgd(cgd)1474return NULL1475cdef int i, j1476for i from 0 <= i < depth:1477cgd.object_stack[i] = allocate_dg_vert(i+degree,depth+degree-1)1478cgd.parent_stack[i] = allocate_dg_vert(i+degree,depth+degree-1)1479if cgd.object_stack[i] is NULL or \1480cgd.parent_stack[i] is NULL:1481for j from 0 <= j <= i:1482free_dg_vert(cgd.object_stack[j])1483free_dg_vert(cgd.parent_stack[j])1484sage_free(dg_vert_gen)1485deallocate_cgd(cgd)1486return NULL1487for i from 0 <= i < depth-1:1488# TODO: in fact, should this not happen in1489# dg_vert_gen_children!? otherwise iterator[i].data will be NULL1490# and no problems.....1491if allocate_subset_gen_2(i+degree, i+degree, cgd.iterator_stack+i):1492for j from 0 <= j < depth:1493free_dg_vert(cgd.object_stack[j])1494free_dg_vert(cgd.parent_stack[j])1495for j from 0 <= j < i:1496cgd2 = <canonical_generator_data *> cgd.iterator_stack[j].data1497deallocate_cgd(cgd2)1498sage_free(dg_vert_gen)1499deallocate_cgd(cgd)1500return NULL1501dg_vert_gen.data = <void *> cgd1502dg_vert_gen.next = &canonical_generator_next1503return dg_vert_gen15041505cdef void free_dg_vert_gen(iterator *dg_vert_gen):1506r"""1507Deallocates the iterator for generating graphs.1508"""1509cdef canonical_generator_data *cgd = <canonical_generator_data *> dg_vert_gen.data1510deallocate_cgd(cgd)1511sage_free(dg_vert_gen)15121513cdef void free_cgd_2(void *data):1514r"""1515A simpler alternative to ``free_cgd``.1516"""1517cdef canonical_generator_data *cgd = <canonical_generator_data *> data1518deallocate_cgd(cgd)15191520def generate_dense_graphs_vert_addition(int n, base_G = None, bint construct = False, bint indicate_mem_err = True):1521r"""15221523EXAMPLES::15241525sage: from sage.groups.perm_gps.partn_ref.refinement_graphs import generate_dense_graphs_vert_addition15261527::15281529sage: for n in [0..7]:1530... generate_dense_graphs_vert_addition(n)153111532215334153481535191536531537209153812531539sage: generate_dense_graphs_vert_addition(8) # long time15401359915411542TEST::15431544sage: from sage.groups.perm_gps.partn_ref.refinement_graphs import generate_dense_graphs_vert_addition1545sage: generate_dense_graphs_vert_addition(10, base_G=Graph('HEhf^rs'))15461115471548"""1549from sage.graphs.all import Graph1550cdef iterator *graph_iterator1551cdef DenseGraph DG, ODG1552cdef GraphStruct GS1553if n < 2:1554if construct:1555L = []1556if n < 0:1557return L1558L.append(Graph(0, implementation='c_graph', sparse=False))1559if n == 0:1560return L1561L.append(Graph(0, implementation='c_graph', sparse=False))1562L.reverse()1563return L1564else:1565if n < 0:1566return Integer(0)1567if n == 0:1568return Integer(1)1569return Integer(2)15701571cdef int start_deg = 1 if base_G is None else base_G.num_verts()1572graph_iterator = allocate_dg_vert_gen(start_deg, n+1-start_deg)1573if graph_iterator is NULL:1574raise MemoryError15751576GS = (<GraphStruct> (<canonical_generator_data *> graph_iterator.data).object_stack[0])1577DG = GS.G1578if base_G is not None:1579for v in base_G.vertices():1580DG.add_vertex(v)1581for u,v in base_G.edges(labels=False):1582DG.add_arc(u,v)1583DG.add_arc(v,u)15841585graph_iterator = setup_canonical_generator(start_deg,1586&all_children_are_equivalent,1587&refine_by_degree,1588&compare_graphs,1589&gen_children_dg_vert,1590&apply_dg_vert_aug,1591&free_dg_vert,1592&free_cgd_2,1593free_subset,1594&canonical_dg_vert_parent,1595n+1-start_deg, 0, graph_iterator)15961597start_canonical_generator(NULL, <void *> GS, DG.num_verts, graph_iterator)15981599cdef list out_list1600cdef void *thing1601cdef GraphStruct thing_gs1602cdef Integer number1603cdef bint mem_err = 01604if construct:1605out_list = []1606else:1607number = Integer(0)1608if construct:1609while 1:1610thing = graph_iterator.next(graph_iterator.data, NULL, &mem_err)1611if thing is NULL: break1612ODG = (<GraphStruct>thing).G1613G = Graph(0, implementation='c_graph', sparse=False)1614DG = DenseGraph(ODG.active_vertices.size, extra_vertices=0)1615copy_dense_graph(DG, ODG)1616G._backend._cg = DG1617out_list.append(G)1618else:1619while 1:1620thing = graph_iterator.next(graph_iterator.data, NULL, &mem_err)1621if thing is NULL: break1622number += 116231624free_dg_vert_gen(graph_iterator)1625if mem_err:1626if indicate_mem_err:1627raise MemoryError1628else:1629out_list.append(MemoryError())1630if construct:1631if base_G is None:1632out_list = [Graph(0, implementation='c_graph', sparse=False)] + out_list1633return out_list1634else:1635if base_G is None:1636number += 11637return number1638163916401641164216431644164516461647164816491650165116521653