Hot-keys on this page
r m x p toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
""" Graph-theoretic partition backtrack functions
EXAMPLES::
sage: import sage.groups.perm_gps.partn_ref.refinement_graphs
REFERENCE:
- [1] McKay, Brendan D. Practical Graph Isomorphism. Congressus Numerantium, Vol. 30 (1981), pp. 45-87. """
#***************************************************************************** # Copyright (C) 2006 - 2011 Robert L. Miller <rlmillster@gmail.com> # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 2 of the License, or # (at your option) any later version. # http://www.gnu.org/licenses/ #*****************************************************************************
from __future__ import print_function
from .data_structures cimport * include "sage/data_structures/bitset.pxi" from sage.rings.integer cimport Integer from sage.graphs.base.sparse_graph cimport SparseGraph from sage.graphs.base.dense_graph cimport DenseGraph from .double_coset cimport double_coset
""" Test whether two graphs are isomorphic.
EXAMPLES::
sage: from sage.groups.perm_gps.partn_ref.refinement_graphs import isomorphic
sage: G = Graph(2) sage: H = Graph(2) sage: isomorphic(G, H, [[0,1]], [0,1], 0, 1) {0: 0, 1: 1} sage: isomorphic(G, H, [[0,1]], [0,1], 0, 1) {0: 0, 1: 1} sage: isomorphic(G, H, [[0],[1]], [0,1], 0, 1) {0: 0, 1: 1} sage: isomorphic(G, H, [[0],[1]], [1,0], 0, 1) {0: 1, 1: 0}
sage: G = Graph(3) sage: H = Graph(3) sage: isomorphic(G, H, [[0,1,2]], [0,1,2], 0, 1) {0: 0, 1: 1, 2: 2} sage: G.add_edge(0,1) sage: isomorphic(G, H, [[0,1,2]], [0,1,2], 0, 1) False sage: H.add_edge(1,2) sage: isomorphic(G, H, [[0,1,2]], [0,1,2], 0, 1) {0: 1, 1: 2, 2: 0} """ cdef PartitionStack *part cdef int *output cdef int *ordering cdef CGraph G cdef GraphStruct GS cdef list partition, cell
else: loops = 1 return False else: G = SparseGraph(n) else: else: return False else: raise TypeError("G must be a Sage graph.")
return {}
PS_dealloc(part) sig_free(ordering) sig_free(output) raise MemoryError
sig_free(GS1.scratch) sig_free(GS2.scratch) PS_dealloc(part) sig_free(ordering) raise MemoryError
else:
verbosity=0, use_indicator_function=True, sparse=True, base=False, order=False): """ Compute canonical labels and automorphism groups of graphs.
INPUT:
- ``G_in`` -- a Sage graph - ``partition`` -- a list of lists representing a partition of the vertices - ``lab`` -- if True, compute and return the canonical label in addition to the automorphism group - ``dig`` -- set to True for digraphs and graphs with loops. If True, does not use optimizations based on Lemma 2.25 in [1] that are valid only for simple graphs. - ``dict_rep`` -- if ``True``, return a dictionary with keys the vertices of the input graph G_in and values elements of the set the permutation group acts on. (The point is that graphs are arbitrarily labelled, often 0..n-1, and permutation groups always act on 1..n. This dictionary maps vertex labels (such as 0..n-1) to the domain of the permutations.) - ``certificate`` -- if ``True``, return the permutation from G to its canonical label. - ``verbosity`` -- currently ignored - ``use_indicator_function`` -- option to turn off indicator function (``True`` is generally faster) - ``sparse`` -- whether to use sparse or dense representation of the graph (ignored if G is already a CGraph - see sage.graphs.base) - ``base`` -- whether to return the first sequence of split vertices (used in computing the order of the group) - ``order`` -- whether to return the order of the automorphism group
OUTPUT:
Depends on the options. If more than one thing is returned, they are in a tuple in the following order:
- list of generators in list-permutation format -- always - dict -- if dict_rep - graph -- if lab - dict -- if certificate - list -- if base - integer -- if order
EXAMPLES::
sage: st = sage.groups.perm_gps.partn_ref.refinement_graphs.search_tree sage: from sage.graphs.base.dense_graph import DenseGraph sage: from sage.graphs.base.sparse_graph import SparseGraph
Graphs on zero vertices::
sage: G = Graph() sage: st(G, [[]], order=True) ([], Graph on 0 vertices, 1)
Graphs on one vertex::
sage: G = Graph(1) sage: st(G, [[0]], order=True) ([], Graph on 1 vertex, 1)
Graphs on two vertices::
sage: G = Graph(2) sage: st(G, [[0,1]], order=True) ([[1, 0]], Graph on 2 vertices, 2) sage: st(G, [[0],[1]], order=True) ([], Graph on 2 vertices, 1) sage: G.add_edge(0,1) sage: st(G, [[0,1]], order=True) ([[1, 0]], Graph on 2 vertices, 2) sage: st(G, [[0],[1]], order=True) ([], Graph on 2 vertices, 1)
Graphs on three vertices::
sage: G = Graph(3) sage: st(G, [[0,1,2]], order=True) ([[0, 2, 1], [1, 0, 2]], Graph on 3 vertices, 6) sage: st(G, [[0],[1,2]], order=True) ([[0, 2, 1]], Graph on 3 vertices, 2) sage: st(G, [[0],[1],[2]], order=True) ([], Graph on 3 vertices, 1) sage: G.add_edge(0,1) sage: st(G, [range(3)], order=True) ([[1, 0, 2]], Graph on 3 vertices, 2) sage: st(G, [[0],[1,2]], order=True) ([], Graph on 3 vertices, 1) sage: st(G, [[0,1],[2]], order=True) ([[1, 0, 2]], Graph on 3 vertices, 2)
The Dodecahedron has automorphism group of size 120::
sage: G = graphs.DodecahedralGraph() sage: Pi = [range(20)] sage: st(G, Pi, order=True)[2] 120
The three-cube has automorphism group of size 48::
sage: G = graphs.CubeGraph(3) sage: G.relabel() sage: Pi = [G.vertices()] sage: st(G, Pi, order=True)[2] 48
We obtain the same output using different types of Sage graphs::
sage: G = graphs.DodecahedralGraph() sage: GD = DenseGraph(20) sage: GS = SparseGraph(20) sage: for i,j,_ in G.edge_iterator(): ....: GD.add_arc(i,j); GD.add_arc(j,i) ....: GS.add_arc(i,j); GS.add_arc(j,i) sage: Pi=[range(20)] sage: a,b = st(G, Pi) sage: asp,bsp = st(GS, Pi) sage: ade,bde = st(GD, Pi) sage: bsg = Graph() sage: bdg = Graph() sage: for i in range(20): ....: for j in range(20): ....: if bsp.has_arc(i,j): ....: bsg.add_edge(i,j) ....: if bde.has_arc(i,j): ....: bdg.add_edge(i,j) sage: a, b.graph6_string() ([[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?@O') sage: a == asp True sage: a == ade True sage: b == bsg True sage: b == bdg True
Cubes!::
sage: C = graphs.CubeGraph(1) sage: gens, order = st(C, [C.vertices()], lab=False, order=True); order 2 sage: C = graphs.CubeGraph(2) sage: gens, order = st(C, [C.vertices()], lab=False, order=True); order 8 sage: C = graphs.CubeGraph(3) sage: gens, order = st(C, [C.vertices()], lab=False, order=True); order 48 sage: C = graphs.CubeGraph(4) sage: gens, order = st(C, [C.vertices()], lab=False, order=True); order 384 sage: C = graphs.CubeGraph(5) sage: gens, order = st(C, [C.vertices()], lab=False, order=True); order 3840 sage: C = graphs.CubeGraph(6) sage: gens, order = st(C, [C.vertices()], lab=False, order=True); order 46080
One can also turn off the indicator function (note: this will take longer)::
sage: D1 = DiGraph({0:[2],2:[0],1:[1]}, loops=True) sage: D2 = DiGraph({1:[2],2:[1],0:[0]}, loops=True) sage: a,b = st(D1, [D1.vertices()], dig=True, use_indicator_function=False) sage: c,d = st(D2, [D2.vertices()], dig=True, use_indicator_function=False) sage: b==d True
This example is due to Chris Godsil::
sage: HS = graphs.HoffmanSingletonGraph() sage: alqs = [Set(c) for c in (HS.complement()).cliques_maximum()] sage: Y = Graph([alqs, lambda s,t: len(s.intersection(t))==0]) sage: Y0,Y1 = Y.connected_components_subgraphs() sage: st(Y0, [Y0.vertices()])[1] == st(Y1, [Y1.vertices()])[1] True sage: st(Y0, [Y0.vertices()])[1] == st(HS, [HS.vertices()])[1] True sage: st(HS, [HS.vertices()])[1] == st(Y1, [Y1.vertices()])[1] True
Certain border cases need to be tested as well::
sage: G = Graph('Fll^G') sage: a,b,c = st(G, [range(G.num_verts())], order=True); b Graph on 7 vertices sage: c 48 sage: G = Graph(21) sage: st(G, [range(G.num_verts())], order=True)[2] == factorial(21) True
sage: G = Graph('^????????????????????{??N??@w??FaGa?PCO@CP?AGa?_QO?Q@G?CcA??cc????Bo????{????F_') sage: perm = {3:15, 15:3} sage: H = G.relabel(perm, inplace=False) sage: st(G, [range(G.num_verts())])[1] == st(H, [range(H.num_verts())])[1] True
sage: st(Graph(':Dkw'), [range(5)], lab=False, dig=True) [[4, 1, 2, 3, 0], [0, 2, 1, 3, 4]]
TESTS::
sage: G = Graph() sage: st(G, [], certify=True) doctest...: DeprecationWarning: use the option 'certificate' instead of 'certify' See http://trac.sagemath.org/21111 for details. ([], Graph on 0 vertices, {}) """ cdef CGraph G cdef int i, j, n cdef Integer I cdef bint loops cdef aut_gp_and_can_lab *output cdef PartitionStack *part else: else: G = DenseGraph(n) else: else: raise TypeError("G must be a Sage graph.")
else: else: G_C = DenseGraph(n) return_tuple.append([]) return return_tuple[0] else:
PS_dealloc(part) sig_free(GS.scratch) raise MemoryError
# prepare output else: else: return_tuple.append([output.group.base_orbits[i][0] for i from 0 <= i < output.group.base_size]) else:
cdef int refine_by_degree(PartitionStack *PS, void *S, int *cells_to_refine_by, int ctrb_len): r""" Refine the input partition by checking degrees of vertices to the given cells.
INPUT:
- ``PS`` -- a partition stack, whose finest partition is the partition to be refined - ``S`` -- a graph struct object, which contains scratch space, the graph in question, and some flags - ``cells_to_refine_by`` -- a list of pointers to cells to check degrees against in refining the other cells (updated in place). Must be allocated to length at least the degree of PS, since the array may grow - ``ctrb_len`` -- how many cells in cells_to_refine_by
OUTPUT:
An integer invariant under the orbits of $S_n$. That is, if $\gamma$ is a permutation of the vertices, then $$ I(G, PS, cells_to_refine_by) = I( \gamma(G), \gamma(PS), \gamma(cells_to_refine_by) ) .$$ """ cdef int current_cell, i, r cdef int first_largest_subcell cdef int max_degree cdef bint necessary_to_split_cell cdef int against_index # should be less verts, then, so place the "nonverts" in separate cell at the end else: # now, i points to the next cell (before refinement) # if we are looking at a digraph, also compute # the reverse degrees and sort by them # now, i points to the next cell (before refinement) else:
cdef int compare_graphs(int *gamma_1, int *gamma_2, void *S1, void *S2, int degree): r""" Compare gamma_1(S1) and gamma_2(S2).
Return return -1 if gamma_1(S1) < gamma_2(S2), 0 if gamma_1(S1) == gamma_2(S2), 1 if gamma_1(S1) > gamma_2(S2). (Just like the python \code{cmp}) function.
INPUT:
- ``gamma_1``, ``gamma_2`` -- list permutations (inverse) - ``S1``, ``S2`` -- graph struct objects """ cdef int i, j, m return G1.has_vertex(gamma_1[i]) - G2.has_vertex(gamma_2[i])
cdef bint all_children_are_equivalent(PartitionStack *PS, void *S): """ Return True if every refinement of the current partition results in the same structure.
WARNING:
Converse does not hold in general! See Lemma 2.25 of [1] for details.
INPUT:
- ``PS`` -- the partition stack to be checked - ``S`` -- a graph struct object """ else:
cdef inline int degree(PartitionStack *PS, CGraph G, int entry, int cell_index, bint reverse): """ Return the number of edges from the vertex corresponding to entry to vertices in the cell corresponding to cell_index.
INPUT:
- ``PS`` -- the partition stack to be checked - ``S`` -- a graph struct object - ``entry`` -- the position of the vertex in question in the entries of PS - ``cell_index`` -- the starting position of the cell in question in the entries of PS - ``reverse`` -- whether to check for arcs in the other direction """ else: else: else:
""" Return all labeled graphs on n vertices {0,1,...,n-1}.
Used in classifying isomorphism types (naive approach), and more importantly in benchmarking the search algorithm.
EXAMPLES::
sage: from sage.groups.perm_gps.partn_ref.refinement_graphs import all_labeled_graphs sage: st = sage.groups.perm_gps.partn_ref.refinement_graphs.search_tree sage: Glist = {} sage: Giso = {} sage: for n in [1..5]: # long time (4s on sage.math, 2011) ....: Glist[n] = all_labeled_graphs(n) ....: Giso[n] = [] ....: for g in Glist[n]: ....: a, b = st(g, [range(n)]) ....: inn = False ....: for gi in Giso[n]: ....: if b == gi: ....: inn = True ....: if not inn: ....: Giso[n].append(b) sage: for n in Giso: # long time ....: print("{} {}".format(n, len(Giso[n]))) 1 1 2 2 3 4 4 11 5 34
"""
""" Tests to make sure that C(gamma(G)) == C(G) for random permutations gamma and random graphs G, and that isomorphic returns an isomorphism.
INPUT:
- ``num`` -- run tests for this many graphs - ``n_max`` -- test graphs with at most this many vertices - ``perms_per_graph`` -- test each graph with this many random permutations
DISCUSSION:
This code generates num random graphs G on at most n_max vertices. The density of edges is chosen randomly between 0 and 1.
For each graph G generated, we uniformly generate perms_per_graph random permutations and verify that the canonical labels of G and the image of G under the generated permutation are equal, and that the isomorphic function returns an isomorphism.
TESTS::
sage: import sage.groups.perm_gps.partn_ref.refinement_graphs sage: sage.groups.perm_gps.partn_ref.refinement_graphs.random_tests() # long time All passed: 200 random tests on 20 graphs. """ from sage.misc.prandom import random, randint from sage.graphs.graph_generators import GraphGenerators from sage.graphs.digraph_generators import DiGraphGenerators from sage.combinat.permutation import Permutations from copy import copy cdef int i, j, num_tests = 0, num_graphs = 0 GG = GraphGenerators() DGG = DiGraphGenerators() for mmm in range(num): p = random() n = randint(1, n_max) S = Permutations(n)
G = GG.RandomGNP(n, p) H = copy(G) for i from 0 <= i < perms_per_graph: G = copy(H) G1 = search_tree(G, [G.vertices()])[1] perm = list(S.random_element()) perm = [perm[j]-1 for j from 0 <= j < n] G.relabel(perm) G2 = search_tree(G, [G.vertices()])[1] if G1 != G2: print("search_tree FAILURE: graph6-") print(H.graph6_string()) print(perm) return isom = isomorphic(G, H, [list(xrange(n))], list(xrange(n)), 0, 1) if not isom or G.relabel(isom, inplace=False) != H: print("isom FAILURE: graph6-") print(H.graph6_string()) print(perm) return
D = DGG.RandomDirectedGNP(n, p) D.allow_loops(True) for i from 0 <= i < n: if random() < p: D.add_edge(i,i) E = copy(D) for i from 0 <= i < perms_per_graph: D = copy(E) D1 = search_tree(D, [D.vertices()], dig=True)[1] perm = list(S.random_element()) perm = [perm[j]-1 for j from 0 <= j < n] D.relabel(perm) D2 = search_tree(D, [D.vertices()], dig=True)[1] if D1 != D2: print("search_tree FAILURE: dig6-") print(E.dig6_string()) print(perm) return isom = isomorphic(D, E, [list(xrange(n))], list(xrange(n)), 1, 1) if not isom or D.relabel(isom, inplace=False) != E: print("isom FAILURE: dig6-") print(E.dig6_string()) print(perm) print(isom) return num_tests += 4*perms_per_graph num_graphs += 2 print("All passed: %d random tests on %d graphs." % (num_tests, num_graphs))
r""" Assuming that G is a graph on vertices 0,1,...,n-1, and gamma is an element of SymmetricGroup(n), returns the partition of the vertex set determined by the orbits of gamma, considered as action on the set 1,2,...,n where we take 0 = n. In other words, returns the partition determined by a cyclic representation of gamma.
INPUT:
- ``list_perm`` - if ``True``, assumes ``gamma`` is a list representing the map `i \mapsto ``gamma``[i]`
EXAMPLES::
sage: from sage.groups.perm_gps.partn_ref.refinement_graphs import orbit_partition sage: G = graphs.PetersenGraph() sage: S = SymmetricGroup(10) sage: gamma = S('(10,1,2,3,4)(5,6,7)(8,9)') sage: orbit_partition(gamma) [[1, 2, 3, 4, 0], [5, 6, 7], [8, 9]] sage: gamma = S('(10,5)(1,6)(2,7)(3,8)(4,9)') sage: orbit_partition(gamma) [[1, 6], [2, 7], [3, 8], [4, 9], [5, 0]] """ n = len(gamma) seen = [1] + [0]*(n-1) i = 0 p = 0 partition = [[0]] while sum(seen) < n: if gamma[i] != partition[p][0]: partition[p].append(gamma[i]) i = gamma[i] seen[i] = 1 else: for j in range(n): if seen[j] == 0: i = j break partition.append([i]) p += 1 seen[i] = 1 return partition else:
""" Return the coarsest equitable refinement of ``partition`` for ``G``.
This is a helper function for the graph function of the same name.
DOCTEST (More thorough testing in ``sage/graphs/graph.py``)::
sage: from sage.groups.perm_gps.partn_ref.refinement_graphs import coarsest_equitable_refinement sage: from sage.graphs.base.sparse_graph import SparseGraph sage: coarsest_equitable_refinement(SparseGraph(7), [[0], [1,2,3,4], [5,6]], 0) [[0], [1, 2, 3, 4], [5, 6]] """
# set up partition stack and graph struct
PS_dealloc(nu) raise MemoryError
# set up cells to refine by PS_dealloc(nu) sig_free(GS.scratch) raise MemoryError
# refine, and get the result
""" Compute orbits given a list of generators of a permutation group, in list format.
This is a helper function for automorphism groups of graphs.
DOCTEST (More thorough testing in ``sage/graphs/graph.py``)::
sage: from sage.groups.perm_gps.partn_ref.refinement_graphs import get_orbits sage: get_orbits([[1,2,3,0,4,5], [0,1,2,3,5,4]], 6) [[0, 1, 2, 3], [4, 5]] """ cdef int i, j
OP_dealloc(OP) raise MemoryError
else:
# Canonical augmentation from cpython.ref cimport *
# Dense graphs: adding edges
# This implements an augmentation scheme as follows: # * Seed objects are graphs with n vertices and no edges. # * Augmentations consist of adding a single edge, or a loop.
cdef void *dg_edge_gen_next(void *data, int *degree, bint *mem_err): r""" The ``next`` function in an edge iterator. The iterator generates unique representatives under the action of the automorphism group of the parent graph on edges not in the graph, which are to considered for adding to the graph. """ cdef subset *edge_candidate cdef int u, v, reject (<canonical_generator_data *> degd.edge_iterator.data).mem_err = 1 else: mem_err[0] = 1
cdef void *allocate_degd(int degree): r""" Allocate the data part of the iterator over edges to add to the graph. """ sig_free(degd) free_subset_gen(edge_iterator) return NULL sig_free(degd) return NULL
cdef void deallocate_degd(void *data): r""" Deallocate the data part of the iterator over edges to add to the graph. """
cdef int gen_children_dg_edge(void *S, aut_gp_and_can_lab *group, iterator *it): r""" Setup an iterator over edges to be added. """
cdef void copy_dense_graph(DenseGraph dest, DenseGraph src): r""" caution! active_vertices must be same size! """
cdef void *apply_dg_edge_aug(void *parent, void *aug, void *child, int *degree, bint *mem_err): r""" Apply the augmentation to ``parent`` storing the result in ``child``. Here ``aug`` represents an edge to be added. """
# copy DG_par edges to DG
# add the edge else:
cdef void *allocate_dg_edge(int n, bint loops): r""" Allocates an object for this augmentation scheme. """ cdef GraphStruct GS cdef DenseGraph G cdef int *scratch raise MemoryError except MemoryError: return NULL
cdef void free_dg_edge(void *child): r""" Deallocates an object for this augmentation scheme. """
cdef void *canonical_dg_edge_parent(void *child, void *parent, int *permutation, int *degree, bint *mem_err): r""" Applies ``permutation`` to ``child``, determines an arbitrary parent by deleting the lexicographically largest edge, applies the inverse of ``permutation`` to the result and stores the result in ``parent``. """
# copy DG edges to DG_par
# remove the right edge
cdef iterator *allocate_dg_edge_gen(int degree, int depth, bint loops): r""" Allocates the iterator for generating graphs. """ sig_free(dg_edge_gen) deallocate_cgd(cgd) return NULL cdef int i, j for j from 0 <= j <= i: deallocate_degd(cgd.iterator_stack[j].data) free_dg_edge(cgd.object_stack[j]) free_dg_edge(cgd.parent_stack[j]) sig_free(dg_edge_gen) deallocate_cgd(cgd) return NULL
cdef void free_dg_edge_gen(iterator *dg_edge_gen): r""" Deallocates the iterator for generating graphs. """
bint indicate_mem_err = True): r"""
EXAMPLES::
sage: from sage.groups.perm_gps.partn_ref.refinement_graphs import generate_dense_graphs_edge_addition
::
sage: for n in [0..6]: ....: print(generate_dense_graphs_edge_addition(n,1)) 1 2 6 20 90 544 5096
::
sage: for n in [0..7]: ....: print(generate_dense_graphs_edge_addition(n,0)) 1 1 2 4 11 34 156 1044 sage: generate_dense_graphs_edge_addition(8,0) # long time - about 14 seconds at 2.4 GHz 12346
""" cdef iterator *graph_iterator cdef DenseGraph DG, ODG cdef GraphStruct GS return [] if construct else Integer(0) else: G = Graph(1, implementation='c_graph', sparse=False, loops=loops) (<CGraph>G._backend._cg).add_arc_unsafe(0,0) return [G, Graph(1, implementation='c_graph', sparse=False, loops=loops)] else:
raise MemoryError
DG = GS.G for u,v in G.edges(labels=False): DG.add_arc(u,v) if u != v: DG.add_arc(v,u)
all_children_are_equivalent, refine_by_degree, compare_graphs, gen_children_dg_edge, apply_dg_edge_aug, free_dg_edge, deallocate_degd, free_subset, canonical_dg_edge_parent,
cdef list out_list cdef void *thing cdef GraphStruct thing_gs cdef Integer number out_list = [] else: while True: thing = graph_iterator.next(graph_iterator.data, NULL, &mem_err) if thing is NULL: break ODG = (<GraphStruct>thing).G G = Graph(0, implementation='c_graph', sparse=False) DG = DenseGraph(ODG.active_vertices.size, extra_vertices=0) copy_dense_graph(DG, ODG) G._backend._cg = DG out_list.append(G) else:
if indicate_mem_err: raise MemoryError else: out_list.append(MemoryError()) return out_list else:
# Dense graphs: adding vertices
# This implements an augmentation scheme as follows: # * Seed objects are graphs with one vertex and no edges. # * Augmentations consist of adding a single vertex connected to some subset of # the previous vertices.
cdef int gen_children_dg_vert(void *S, aut_gp_and_can_lab *group, iterator *it): r""" Setup an iterator over subsets to join a new vertex to. """
cdef void *apply_dg_vert_aug(void *parent, void *aug, void *child, int *degree, bint *mem_err): r""" Apply the augmentation to ``parent`` storing the result in ``child``. Here ``aug`` represents a subset to join to a new vertex. """
# copy DG_par edges to DG
# add the edges
cdef void *allocate_dg_vert(int n, int depth): r""" Allocates an object for this augmentation scheme. """ cdef GraphStruct GS cdef DenseGraph G cdef int *scratch raise MemoryError except MemoryError: return NULL
cdef void free_dg_vert(void *child): r""" Deallocates an object for this augmentation scheme. """
cdef void *canonical_dg_vert_parent(void *child, void *parent, int *permutation, int *degree, bint *mem_err): r""" Applies ``permutation`` to ``child``, determines an arbitrary parent by deleting the lexicographically largest vertex, applies the inverse of ``permutation`` to the result and stores the result in ``parent``. """
# copy DG edges to DG_par
# remove the right vertex
cdef iterator *allocate_dg_vert_gen(int degree, int depth): r""" Allocates the iterator for generating graphs. """ cdef canonical_generator_data *cgd2 sig_free(dg_vert_gen) deallocate_cgd(cgd) return NULL cdef int i, j for j from 0 <= j <= i: free_dg_vert(cgd.object_stack[j]) free_dg_vert(cgd.parent_stack[j]) sig_free(dg_vert_gen) deallocate_cgd(cgd) return NULL # TODO: in fact, should this not happen in # dg_vert_gen_children!? otherwise iterator[i].data will be NULL # and no problems..... for j from 0 <= j < depth: free_dg_vert(cgd.object_stack[j]) free_dg_vert(cgd.parent_stack[j]) for j from 0 <= j < i: cgd2 = <canonical_generator_data *> cgd.iterator_stack[j].data deallocate_cgd(cgd2) sig_free(dg_vert_gen) deallocate_cgd(cgd) return NULL
cdef void free_dg_vert_gen(iterator *dg_vert_gen): r""" Deallocates the iterator for generating graphs. """
cdef void free_cgd_2(void *data): r""" A simpler alternative to ``free_cgd``. """
r"""
EXAMPLES::
sage: from sage.groups.perm_gps.partn_ref.refinement_graphs import generate_dense_graphs_vert_addition
::
sage: for n in [0..7]: ....: generate_dense_graphs_vert_addition(n) 1 2 4 8 19 53 209 1253 sage: generate_dense_graphs_vert_addition(8) # long time 13599
TESTS::
sage: from sage.groups.perm_gps.partn_ref.refinement_graphs import generate_dense_graphs_vert_addition sage: generate_dense_graphs_vert_addition(10, base_G=Graph('HEhf^rs')) 11
""" cdef iterator *graph_iterator cdef DenseGraph DG, ODG cdef GraphStruct GS L = [] if n < 0: return L L.append(Graph(0, implementation='c_graph', sparse=False)) if n == 0: return L L.append(Graph(0, implementation='c_graph', sparse=False)) L.reverse() return L else: return Integer(0)
raise MemoryError
all_children_are_equivalent, refine_by_degree, compare_graphs, gen_children_dg_vert, apply_dg_vert_aug, free_dg_vert, free_cgd_2, free_subset, canonical_dg_vert_parent, n+1-start_deg, 0, graph_iterator)
cdef list out_list cdef void *thing cdef GraphStruct thing_gs cdef Integer number out_list = [] else: while True: thing = graph_iterator.next(graph_iterator.data, NULL, &mem_err) if thing is NULL: break ODG = (<GraphStruct>thing).G G = Graph(0, implementation='c_graph', sparse=False) DG = DenseGraph(ODG.active_vertices.size, extra_vertices=0) copy_dense_graph(DG, ODG) G._backend._cg = DG out_list.append(G) else:
if indicate_mem_err: raise MemoryError else: out_list.append(MemoryError()) if base_G is None: out_list = [Graph(0, implementation='c_graph', sparse=False)] + out_list return out_list else: |