| | 1 | r""" |
| | 2 | Partition refinement algorithms for computing automorphism groups and canonical |
| | 3 | labels of various objects. |
| | 4 | |
| | 5 | This module implements a general algorithm for computing automorphism groups and |
| | 6 | canonical labels of objects. The class of objects in question must be some kind |
| | 7 | of structure for which an automorphism is a permutation in $S_n$ for some $n$, |
| | 8 | which we call here the order of the object. It should be noted that the word |
| | 9 | "canonical" in the term canonical label is used loosely. Given an object $X$, |
| | 10 | the canonical label $C(X)$ is just an object for which $C(X) = C(Y)$ for any |
| | 11 | $Y$ such that $X \cong Y$. |
| | 12 | |
| | 13 | To understand the algorithm better, a few definitions will help. First one |
| | 14 | should note that throughout this module, $S_n$ is defined as the set of |
| | 15 | permutations of the set $[n] := \{0, 1, ..., n-1\}$. One speaks of |
| | 16 | ordered partitions $\Pi = (C_1, ..., C_k)$ of $[n]$. The $C_i$ are disjoint |
| | 17 | subsets of $[n]$ whose union is equal to $[n]$, and we call them cells. A |
| | 18 | partition $\Pi_1$ is said to be finer than another partition $\Pi_2$ if every |
| | 19 | cell of $\Pi_1$ is a subset of some cell of $\Pi_2$ (in this situation we also |
| | 20 | say that $\Pi_2$ is coarser than $\Pi_1$). A partition stack |
| | 21 | $\nu = (\Pi_0, ..., \Pi_k)$ is a sequence of partitions such that $\Pi_i$ is |
| | 22 | finer than $\Pi_{i-1}$ for each $i$ such that $1 \leq i \leq k$. Sometimes these |
| | 23 | are called nested partitions. The depth of $\nu$ is defined to be $k$ and the |
| | 24 | degree of $\nu$ is defined to be $n$. Partition stacks are implemented as |
| | 25 | \code{PartitionStack} (see automorphism_group_canonical_label.pxd for more |
| | 26 | information). Finally, we say that a permutation respects the partition $\Pi$ if |
| | 27 | its orbits form a partition which is finer than $\Pi$. |
| | 28 | |
| | 29 | In order to take advantage of the algorithms in this module for a specific kind |
| | 30 | of object, one must implement (in Cython) three functions which will be specific |
| | 31 | to the kind of objects in question. Pointers to these functions are passed to |
| | 32 | the main function of the module, which is \code{get_aut_gp_and_can_lab}. For |
| | 33 | specific examples of implementations of these functions, see any of the files in |
| | 34 | \code{sage.groups.perm_gps.partn_ref} beginning with "refinement." They are: |
| | 35 | |
| | 36 | A. \code{refine_and_return_invariant}: |
| | 37 | |
| | 38 | Signature: |
| | 39 | |
| | 40 | \code{int refine_and_return_invariant(PartitionStack *PS, object S, int *cells_to_refine_by, int ctrb_len)} |
| | 41 | |
| | 42 | This function should split up cells in the partition at the top of the |
| | 43 | partition stack in such a way that any automorphism that respects the |
| | 44 | partition also respects the resulting partition. The array |
| | 45 | cells_to_refine_by is a list of the beginning positions of some cells which |
| | 46 | have been changed since the last refinement. It is not necessary to use |
| | 47 | this in an implementation of this function, but it will affect performance. |
| | 48 | One should consult \code{refinement_graphs} for more details and ideas for |
| | 49 | particular implementations. |
| | 50 | |
| | 51 | Output: |
| | 52 | |
| | 53 | An integer $I$ invariant under the orbits of $S_n$. That is, if |
| | 54 | $\gamma \in S_n$, then |
| | 55 | $$ I(G, PS, cells_to_refine_by) = I( \gamma(G), \gamma(PS), \gamma(cells_to_refine_by) ) .$$ |
| | 56 | |
| | 57 | |
| | 58 | B. \code{compare_structures}: |
| | 59 | |
| | 60 | Signature: |
| | 61 | |
| | 62 | \code{int compare_structures(int *gamma_1, int *gamma_2, object S)} |
| | 63 | |
| | 64 | This function must implement a total ordering on the set of objects of fixed |
| | 65 | order. Return -1 if \code{gamma_1(S) < gamma_2(S)}, 0 if |
| | 66 | \code{gamma_1(S) == gamma_2(S)}, 1 if \code{gamma_1(S) > gamma_2(S)}. |
| | 67 | |
| | 68 | C. \code{all_children_are_equivalent}: |
| | 69 | |
| | 70 | Signature: |
| | 71 | |
| | 72 | \code{bint all_children_are_equivalent(PartitionStack *PS, object S)} |
| | 73 | |
| | 74 | This function must return False unless it is the case that each discrete |
| | 75 | partition finer than the top of the partition stack is equivalent to the |
| | 76 | others under some automorphism of S. The converse need not hold: if this is |
| | 77 | indeed the case, it still may return False. This function is originally used |
| | 78 | as a consequence of Lemma 2.25 in [1]. |
| | 79 | |
| | 80 | DOCTEST: |
| | 81 | sage: import sage.groups.perm_gps.partn_ref.automorphism_group_canonical_label |
| | 82 | |
| | 83 | REFERENCE: |
| | 84 | |
| | 85 | [1] McKay, Brendan D. Practical Graph Isomorphism. Congressus Numerantium, |
| | 86 | Vol. 30 (1981), pp. 45-87. |
| | 87 | |
| | 88 | """ |
| | 89 | |
| | 90 | #***************************************************************************** |
| | 91 | # Copyright (C) 2006 - 2008 Robert L. Miller <rlmillster@gmail.com> |
| | 92 | # |
| | 93 | # Distributed under the terms of the GNU General Public License (GPL) |
| | 94 | # http://www.gnu.org/licenses/ |
| | 95 | #***************************************************************************** |
| | 96 | |
| | 97 | include '../../../misc/bitset.pxi' |
| | 98 | |
| | 99 | cdef inline int min(int a, int b): |
| | 100 | if a < b: return a |
| | 101 | else: return b |
| | 102 | |
| | 103 | cdef inline int max(int a, int b): |
| | 104 | if a > b: return a |
| | 105 | else: return b |
| | 106 | |
| | 107 | cdef inline int cmp(int a, int b): |
| | 108 | if a < b: return -1 |
| | 109 | elif a == b: return 0 |
| | 110 | else: return 1 |
| | 111 | |
| | 112 | # OrbitPartitions |
| | 113 | |
| | 114 | cdef inline OrbitPartition *OP_new(int n): |
| | 115 | """ |
| | 116 | Allocate and return a pointer to a new OrbitPartition of degree n. Returns a |
| | 117 | null pointer in the case of an allocation failure. |
| | 118 | """ |
| | 119 | cdef int i |
| | 120 | cdef OrbitPartition *OP = <OrbitPartition *> \ |
| | 121 | sage_malloc(sizeof(OrbitPartition)) |
| | 122 | if OP is NULL: |
| | 123 | return OP |
| | 124 | OP.degree = n |
| | 125 | OP.parent = <int *> sage_malloc( n * sizeof(int) ) |
| | 126 | OP.rank = <int *> sage_malloc( n * sizeof(int) ) |
| | 127 | OP.mcr = <int *> sage_malloc( n * sizeof(int) ) |
| | 128 | OP.size = <int *> sage_malloc( n * sizeof(int) ) |
| | 129 | if OP.parent is NULL or OP.rank is NULL or \ |
| | 130 | OP.mcr is NULL or OP.size is NULL: |
| | 131 | if OP.parent is not NULL: sage_free(OP.parent) |
| | 132 | if OP.rank is not NULL: sage_free(OP.rank) |
| | 133 | if OP.mcr is not NULL: sage_free(OP.mcr) |
| | 134 | if OP.size is not NULL: sage_free(OP.size) |
| | 135 | return NULL |
| | 136 | for i from 0 <= i < n: |
| | 137 | OP.parent[i] = i |
| | 138 | OP.rank[i] = 0 |
| | 139 | OP.mcr[i] = i |
| | 140 | OP.size[i] = 1 |
| | 141 | return OP |
| | 142 | |
| | 143 | cdef inline int OP_dealloc(OrbitPartition *OP): |
| | 144 | sage_free(OP.parent) |
| | 145 | sage_free(OP.rank) |
| | 146 | sage_free(OP.mcr) |
| | 147 | sage_free(OP.size) |
| | 148 | sage_free(OP) |
| | 149 | return 0 |
| | 150 | |
| | 151 | cdef inline int OP_find(OrbitPartition *OP, int n): |
| | 152 | """ |
| | 153 | Report the representative ("root") of the cell which contains n. |
| | 154 | """ |
| | 155 | if OP.parent[n] == n: |
| | 156 | return n |
| | 157 | else: |
| | 158 | OP.parent[n] = OP_find(OP, OP.parent[n]) |
| | 159 | return OP.parent[n] |
| | 160 | |
| | 161 | cdef inline int OP_join(OrbitPartition *OP, int m, int n): |
| | 162 | """ |
| | 163 | Join the cells containing m and n, if they are different. |
| | 164 | """ |
| | 165 | cdef int m_root = OP_find(OP, m) |
| | 166 | cdef int n_root = OP_find(OP, n) |
| | 167 | if OP.rank[m_root] > OP.rank[n_root]: |
| | 168 | OP.parent[n_root] = m_root |
| | 169 | OP.mcr[m_root] = min(OP.mcr[m_root], OP.mcr[n_root]) |
| | 170 | OP.size[m_root] += OP.size[n_root] |
| | 171 | elif OP.rank[m_root] < OP.rank[n_root]: |
| | 172 | OP.parent[m_root] = n_root |
| | 173 | OP.mcr[n_root] = min(OP.mcr[m_root], OP.mcr[n_root]) |
| | 174 | OP.size[n_root] += OP.size[m_root] |
| | 175 | elif m_root != n_root: |
| | 176 | OP.parent[n_root] = m_root |
| | 177 | OP.mcr[m_root] = min(OP.mcr[m_root], OP.mcr[n_root]) |
| | 178 | OP.size[m_root] += OP.size[n_root] |
| | 179 | OP.rank[m_root] += 1 |
| | 180 | |
| | 181 | cdef inline int OP_merge_list_perm(OrbitPartition *OP, int *gamma): |
| | 182 | """ |
| | 183 | Joins the cells of OP which intersect the same orbit of gamma. |
| | 184 | |
| | 185 | INPUT: |
| | 186 | gamma - an integer array representing i -> gamma[i]. |
| | 187 | |
| | 188 | OUTPUT: |
| | 189 | 1 - something changed |
| | 190 | 0 - orbits of gamma all contained in cells of OP |
| | 191 | """ |
| | 192 | cdef int i, i_root, gamma_i_root, changed = 0 |
| | 193 | for i from 0 <= i < OP.degree: |
| | 194 | if gamma[i] == i: continue |
| | 195 | i_root = OP_find(OP, i) |
| | 196 | gamma_i_root = OP_find(OP, gamma[i]) |
| | 197 | if i_root != gamma_i_root: |
| | 198 | changed = 1 |
| | 199 | OP_join(OP, i_root, gamma_i_root) |
| | 200 | return changed |
| | 201 | |
| | 202 | def OP_represent(int n=9, merges=[(0,1),(2,3),(3,4)], perm=[1,2,0,4,3,6,7,5,8]): |
| | 203 | """ |
| | 204 | Demonstration and testing. |
| | 205 | |
| | 206 | DOCTEST: |
| | 207 | sage: from sage.groups.perm_gps.partn_ref.automorphism_group_canonical_label \ |
| | 208 | ... import OP_represent |
| | 209 | sage: OP_represent() |
| | 210 | Allocating OrbitPartition... |
| | 211 | Allocation passed. |
| | 212 | Checking that each element reports itself as its root. |
| | 213 | Each element reports itself as its root. |
| | 214 | Merging: |
| | 215 | Merged 0 and 1. |
| | 216 | Merged 2 and 3. |
| | 217 | Merged 3 and 4. |
| | 218 | Done merging. |
| | 219 | Finding: |
| | 220 | 0 -> 0, root: size=2, mcr=0, rank=1 |
| | 221 | 1 -> 0 |
| | 222 | 2 -> 2, root: size=3, mcr=2, rank=1 |
| | 223 | 3 -> 2 |
| | 224 | 4 -> 2 |
| | 225 | 5 -> 5, root: size=1, mcr=5, rank=0 |
| | 226 | 6 -> 6, root: size=1, mcr=6, rank=0 |
| | 227 | 7 -> 7, root: size=1, mcr=7, rank=0 |
| | 228 | 8 -> 8, root: size=1, mcr=8, rank=0 |
| | 229 | Allocating array to test merge_perm. |
| | 230 | Allocation passed. |
| | 231 | Merging permutation: [1, 2, 0, 4, 3, 6, 7, 5, 8] |
| | 232 | Done merging. |
| | 233 | Finding: |
| | 234 | 0 -> 0, root: size=5, mcr=0, rank=2 |
| | 235 | 1 -> 0 |
| | 236 | 2 -> 0 |
| | 237 | 3 -> 0 |
| | 238 | 4 -> 0 |
| | 239 | 5 -> 5, root: size=3, mcr=5, rank=1 |
| | 240 | 6 -> 5 |
| | 241 | 7 -> 5 |
| | 242 | 8 -> 8, root: size=1, mcr=8, rank=0 |
| | 243 | Deallocating OrbitPartition. |
| | 244 | Done. |
| | 245 | |
| | 246 | """ |
| | 247 | cdef int i |
| | 248 | print "Allocating OrbitPartition..." |
| | 249 | cdef OrbitPartition *OP = OP_new(n) |
| | 250 | if OP is NULL: |
| | 251 | print "Allocation failed!" |
| | 252 | return |
| | 253 | print "Allocation passed." |
| | 254 | print "Checking that each element reports itself as its root." |
| | 255 | good = True |
| | 256 | for i from 0 <= i < n: |
| | 257 | if not OP_find(OP, i) == i: |
| | 258 | print "Failed at i = %d!"%i |
| | 259 | good = False |
| | 260 | if good: print "Each element reports itself as its root." |
| | 261 | print "Merging:" |
| | 262 | for i,j in merges: |
| | 263 | OP_join(OP, i, j) |
| | 264 | print "Merged %d and %d."%(i,j) |
| | 265 | print "Done merging." |
| | 266 | print "Finding:" |
| | 267 | for i from 0 <= i < n: |
| | 268 | j = OP_find(OP, i) |
| | 269 | s = "%d -> %d"%(i, j) |
| | 270 | if i == j: |
| | 271 | s += ", root: size=%d, mcr=%d, rank=%d"%\ |
| | 272 | (OP.size[i], OP.mcr[i], OP.rank[i]) |
| | 273 | print s |
| | 274 | print "Allocating array to test merge_perm." |
| | 275 | cdef int *gamma = <int *> sage_malloc( n * sizeof(int) ) |
| | 276 | if gamma is NULL: |
| | 277 | print "Allocation failed!" |
| | 278 | OP_dealloc(OP) |
| | 279 | return |
| | 280 | print "Allocation passed." |
| | 281 | for i from 0 <= i < n: |
| | 282 | gamma[i] = perm[i] |
| | 283 | print "Merging permutation: %s"%perm |
| | 284 | OP_merge_list_perm(OP, gamma) |
| | 285 | print "Done merging." |
| | 286 | print "Finding:" |
| | 287 | for i from 0 <= i < n: |
| | 288 | j = OP_find(OP, i) |
| | 289 | s = "%d -> %d"%(i, j) |
| | 290 | if i == j: |
| | 291 | s += ", root: size=%d, mcr=%d, rank=%d"%\ |
| | 292 | (OP.size[i], OP.mcr[i], OP.rank[i]) |
| | 293 | print s |
| | 294 | print "Deallocating OrbitPartition." |
| | 295 | sage_free(gamma) |
| | 296 | OP_dealloc(OP) |
| | 297 | print "Done." |
| | 298 | |
| | 299 | # PartitionStacks |
| | 300 | |
| | 301 | cdef inline PartitionStack *PS_new(int n, bint unit_partition): |
| | 302 | """ |
| | 303 | Allocate and return a pointer to a new PartitionStack of degree n. Returns a |
| | 304 | null pointer in the case of an allocation failure. |
| | 305 | """ |
| | 306 | cdef int i |
| | 307 | cdef PartitionStack *PS = <PartitionStack *> \ |
| | 308 | sage_malloc(sizeof(PartitionStack)) |
| | 309 | if PS is NULL: |
| | 310 | return PS |
| | 311 | PS.entries = <int *> sage_malloc( n * sizeof(int) ) |
| | 312 | PS.levels = <int *> sage_malloc( n * sizeof(int) ) |
| | 313 | if PS.entries is NULL or PS.levels is NULL: |
| | 314 | if PS.entries is not NULL: sage_free(PS.entries) |
| | 315 | if PS.levels is not NULL: sage_free(PS.levels) |
| | 316 | return NULL |
| | 317 | PS.depth = 0 |
| | 318 | PS.degree = n |
| | 319 | if unit_partition: |
| | 320 | for i from 0 <= i < n-1: |
| | 321 | PS.entries[i] = i |
| | 322 | PS.levels[i] = n |
| | 323 | PS.entries[n-1] = n-1 |
| | 324 | PS.levels[n-1] = -1 |
| | 325 | return PS |
| | 326 | |
| | 327 | cdef inline PartitionStack *PS_copy(PartitionStack *PS): |
| | 328 | """ |
| | 329 | Allocate and return a pointer to a copy of PartitionStack PS. Returns a null |
| | 330 | pointer in the case of an allocation failure. |
| | 331 | """ |
| | 332 | cdef int i |
| | 333 | cdef PartitionStack *PS2 = <PartitionStack *> \ |
| | 334 | sage_malloc(sizeof(PartitionStack)) |
| | 335 | if PS2 is NULL: |
| | 336 | return PS2 |
| | 337 | PS2.entries = <int *> sage_malloc( PS.degree * sizeof(int) ) |
| | 338 | PS2.levels = <int *> sage_malloc( PS.degree * sizeof(int) ) |
| | 339 | if PS2.entries is NULL or PS2.levels is NULL: |
| | 340 | if PS2.entries is not NULL: sage_free(PS2.entries) |
| | 341 | if PS2.levels is not NULL: sage_free(PS2.levels) |
| | 342 | return NULL |
| | 343 | PS_copy_from_to(PS, PS2) |
| | 344 | return PS2 |
| | 345 | |
| | 346 | cdef inline int PS_copy_from_to(PartitionStack *PS, PartitionStack *PS2): |
| | 347 | """ |
| | 348 | Copy all data from PS to PS2. |
| | 349 | """ |
| | 350 | PS2.depth = PS.depth |
| | 351 | PS2.degree = PS.degree |
| | 352 | for i from 0 <= i < PS.degree: |
| | 353 | PS2.entries[i] = PS.entries[i] |
| | 354 | PS2.levels[i] = PS.levels[i] |
| | 355 | |
| | 356 | cdef inline PartitionStack *PS_from_list(object L): |
| | 357 | """ |
| | 358 | Allocate and return a pointer to a PartitionStack representing L. Returns a |
| | 359 | null pointer in the case of an allocation failure. |
| | 360 | """ |
| | 361 | cdef int cell, i, num_cells = len(L), cur_start = 0, cur_len, n = 0 |
| | 362 | for cell from 0 <= cell < num_cells: |
| | 363 | n += len(L[cell]) |
| | 364 | cdef PartitionStack *PS = PS_new(n, 0) |
| | 365 | cell = 0 |
| | 366 | if PS is NULL: |
| | 367 | return PS |
| | 368 | while 1: |
| | 369 | cur_len = len(L[cell]) |
| | 370 | for i from 0 <= i < cur_len: |
| | 371 | PS.entries[cur_start + i] = L[cell][i] |
| | 372 | PS.levels[cur_start + i] = n |
| | 373 | PS_move_min_to_front(PS, cur_start, cur_start+cur_len-1) |
| | 374 | cur_start += cur_len |
| | 375 | cell += 1 |
| | 376 | if cell == num_cells: |
| | 377 | PS.levels[cur_start-1] = -1 |
| | 378 | break |
| | 379 | PS.levels[cur_start-1] = 0 |
| | 380 | return PS |
| | 381 | |
| | 382 | cdef inline int PS_dealloc(PartitionStack *PS): |
| | 383 | sage_free(PS.entries) |
| | 384 | sage_free(PS.levels) |
| | 385 | sage_free(PS) |
| | 386 | return 0 |
| | 387 | |
| | 388 | cdef inline int PS_print(PartitionStack *PS): |
| | 389 | """ |
| | 390 | Print a visual representation of PS. |
| | 391 | """ |
| | 392 | cdef int i |
| | 393 | for i from 0 <= i < PS.depth + 1: |
| | 394 | PS_print_partition(PS, i) |
| | 395 | print |
| | 396 | |
| | 397 | cdef inline int PS_print_partition(PartitionStack *PS, int k): |
| | 398 | """ |
| | 399 | Print the partition at depth k. |
| | 400 | """ |
| | 401 | s = '(' |
| | 402 | for i from 0 <= i < PS.degree: |
| | 403 | s += str(PS.entries[i]) |
| | 404 | if PS.levels[i] <= k: |
| | 405 | s += '|' |
| | 406 | else: |
| | 407 | s += ',' |
| | 408 | s = s[:-1] + ')' |
| | 409 | print s |
| | 410 | |
| | 411 | cdef inline bint PS_is_discrete(PartitionStack *PS): |
| | 412 | """ |
| | 413 | Returns whether the deepest partition consists only of singleton cells. |
| | 414 | """ |
| | 415 | cdef int i |
| | 416 | for i from 0 <= i < PS.degree: |
| | 417 | if PS.levels[i] > PS.depth: |
| | 418 | return 0 |
| | 419 | return 1 |
| | 420 | |
| | 421 | cdef inline int PS_num_cells(PartitionStack *PS): |
| | 422 | """ |
| | 423 | Returns the number of cells. |
| | 424 | """ |
| | 425 | cdef int i, ncells = 0 |
| | 426 | for i from 0 <= i < PS.degree: |
| | 427 | if PS.levels[i] <= PS.depth: |
| | 428 | ncells += 1 |
| | 429 | return ncells |
| | 430 | |
| | 431 | cdef inline int PS_move_min_to_front(PartitionStack *PS, int start, int end): |
| | 432 | """ |
| | 433 | Makes sure that the first element of the segment of entries i with |
| | 434 | start <= i <= end is minimal. |
| | 435 | """ |
| | 436 | cdef int i, min_loc = start, minimum = PS.entries[start] |
| | 437 | for i from start < i <= end: |
| | 438 | if PS.entries[i] < minimum: |
| | 439 | min_loc = i |
| | 440 | minimum = PS.entries[i] |
| | 441 | if min_loc != start: |
| | 442 | PS.entries[min_loc] = PS.entries[start] |
| | 443 | PS.entries[start] = minimum |
| | 444 | |
| | 445 | cdef inline bint PS_is_mcr(PartitionStack *PS, int m): |
| | 446 | """ |
| | 447 | Returns whether PS.elements[m] (not m!) is the smallest element of its cell. |
| | 448 | """ |
| | 449 | return m == 0 or PS.levels[m-1] <= PS.depth |
| | 450 | |
| | 451 | cdef inline bint PS_is_fixed(PartitionStack *PS, int m): |
| | 452 | """ |
| | 453 | Returns whether PS.elements[m] (not m!) is in a singleton cell, assuming |
| | 454 | PS_is_mcr(PS, m) is already true. |
| | 455 | """ |
| | 456 | return PS.levels[m] <= PS.depth |
| | 457 | |
| | 458 | cdef inline int PS_clear(PartitionStack *PS): |
| | 459 | """ |
| | 460 | Sets the current partition to the first shallower one, i.e. forgets about |
| | 461 | boundaries between cells that are new to the current level. |
| | 462 | """ |
| | 463 | cdef int i, cur_start = 0 |
| | 464 | for i from 0 <= i < PS.degree: |
| | 465 | if PS.levels[i] >= PS.depth: |
| | 466 | PS.levels[i] += 1 |
| | 467 | if PS.levels[i] < PS.depth: |
| | 468 | PS_move_min_to_front(PS, cur_start, i) |
| | 469 | cur_start = i+1 |
| | 470 | |
| | 471 | cdef inline int PS_split_point(PartitionStack *PS, int v): |
| | 472 | """ |
| | 473 | Detaches the point v from the cell it is in, putting the singleton cell |
| | 474 | of just v in front. Returns the position where v is now located. |
| | 475 | """ |
| | 476 | cdef int i = 0, index_of_v |
| | 477 | while PS.entries[i] != v: |
| | 478 | i += 1 |
| | 479 | index_of_v = i |
| | 480 | while PS.levels[i] > PS.depth: |
| | 481 | i += 1 |
| | 482 | if (index_of_v == 0 or PS.levels[index_of_v-1] <= PS.depth) \ |
| | 483 | and PS.levels[index_of_v] > PS.depth: |
| | 484 | # if v is first (make sure v is not already alone) |
| | 485 | PS_move_min_to_front(PS, index_of_v+1, i) |
| | 486 | PS.levels[index_of_v] = PS.depth |
| | 487 | return index_of_v |
| | 488 | else: |
| | 489 | # If v is not at front, i.e. v is not minimal in its cell, |
| | 490 | # then move_min_to_front is not necessary since v will swap |
| | 491 | # with the first before making its own cell, leaving it at |
| | 492 | # the front of the other. |
| | 493 | i = index_of_v |
| | 494 | while i != 0 and PS.levels[i-1] > PS.depth: |
| | 495 | i -= 1 |
| | 496 | PS.entries[index_of_v] = PS.entries[i+1] # move the second element to v |
| | 497 | PS.entries[i+1] = PS.entries[i] # move the first (min) to second |
| | 498 | PS.entries[i] = v # place v first |
| | 499 | PS.levels[i] = PS.depth |
| | 500 | return i |
| | 501 | |
| | 502 | cdef inline int PS_first_smallest(PartitionStack *PS, bitset_t b): |
| | 503 | """ |
| | 504 | Find the first occurrence of the smallest cell of size greater than one, |
| | 505 | store its entries to b, and return its minimum element. |
| | 506 | """ |
| | 507 | cdef int i = 0, j = 0, location = 0, n = PS.degree |
| | 508 | bitset_zero(b) |
| | 509 | while 1: |
| | 510 | if PS.levels[i] <= PS.depth: |
| | 511 | if i != j and n > i - j + 1: |
| | 512 | n = i - j + 1 |
| | 513 | location = j |
| | 514 | j = i + 1 |
| | 515 | if PS.levels[i] == -1: break |
| | 516 | i += 1 |
| | 517 | # location now points to the beginning of the first, smallest, |
| | 518 | # nontrivial cell |
| | 519 | i = location |
| | 520 | while 1: |
| | 521 | bitset_flip(b, PS.entries[i]) |
| | 522 | if PS.levels[i] <= PS.depth: break |
| | 523 | i += 1 |
| | 524 | return PS.entries[location] |
| | 525 | |
| | 526 | cdef inline int PS_get_perm_from(PartitionStack *PS1, PartitionStack *PS2, int *gamma): |
| | 527 | """ |
| | 528 | Store the permutation determined by PS2[i] -> PS1[i] for each i, where PS[i] |
| | 529 | denotes the entry of the ith cell of the discrete partition PS. |
| | 530 | """ |
| | 531 | cdef int i = 0 |
| | 532 | for i from 0 <= i < PS1.degree: |
| | 533 | gamma[PS2.entries[i]] = PS1.entries[i] |
| | 534 | |
| | 535 | def PS_represent(partition=[[6],[3,4,8,7],[1,9,5],[0,2]], splits=[6,1,8,2]): |
| | 536 | """ |
| | 537 | Demonstration and testing. |
| | 538 | |
| | 539 | DOCTEST: |
| | 540 | sage: from sage.groups.perm_gps.partn_ref.automorphism_group_canonical_label \ |
| | 541 | ... import PS_represent |
| | 542 | sage: PS_represent() |
| | 543 | Allocating PartitionStack... |
| | 544 | Allocation passed: |
| | 545 | (0,1,2,3,4,5,6,7,8,9) |
| | 546 | Checking that entries are in order and correct level. |
| | 547 | Everything seems in order, deallocating. |
| | 548 | Deallocated. |
| | 549 | Creating PartitionStack from partition [[6], [3, 4, 8, 7], [1, 9, 5], [0, 2]]. |
| | 550 | PartitionStack's data: |
| | 551 | entries -> [6, 3, 4, 8, 7, 1, 9, 5, 0, 2] |
| | 552 | levels -> [0, 10, 10, 10, 0, 10, 10, 0, 10, -1] |
| | 553 | depth = 0, degree = 10 |
| | 554 | (6|3,4,8,7|1,9,5|0,2) |
| | 555 | Checking PS_is_discrete: |
| | 556 | False |
| | 557 | Checking PS_num_cells: |
| | 558 | 4 |
| | 559 | Checking PS_is_mcr, min cell reps are: |
| | 560 | [6, 3, 1, 0] |
| | 561 | Checking PS_is_fixed, fixed elements are: |
| | 562 | [6] |
| | 563 | Copying PartitionStack: |
| | 564 | (6|3,4,8,7|1,9,5|0,2) |
| | 565 | Checking for consistency. |
| | 566 | Everything is consistent. |
| | 567 | Clearing copy: |
| | 568 | (0,3,4,8,7,1,9,5,6,2) |
| | 569 | Splitting point 6 from original: |
| | 570 | 0 |
| | 571 | (6|3,4,8,7|1,9,5|0,2) |
| | 572 | Splitting point 1 from original: |
| | 573 | 5 |
| | 574 | (6|3,4,8,7|1|5,9|0,2) |
| | 575 | Splitting point 8 from original: |
| | 576 | 1 |
| | 577 | (6|8|3,4,7|1|5,9|0,2) |
| | 578 | Splitting point 2 from original: |
| | 579 | 8 |
| | 580 | (6|8|3,4,7|1|5,9|2|0) |
| | 581 | Getting permutation from PS2->PS: |
| | 582 | [6, 1, 0, 8, 3, 9, 2, 7, 4, 5] |
| | 583 | Finding first smallest: |
| | 584 | Minimal element is 5, bitset is: |
| | 585 | 0000010001 |
| | 586 | Deallocating PartitionStacks. |
| | 587 | Done. |
| | 588 | |
| | 589 | """ |
| | 590 | cdef int i, n = sum([len(cell) for cell in partition]), *gamma |
| | 591 | cdef bitset_t b |
| | 592 | print "Allocating PartitionStack..." |
| | 593 | cdef PartitionStack *PS = PS_new(n, 1), *PS2 |
| | 594 | if PS is NULL: |
| | 595 | print "Allocation failed!" |
| | 596 | return |
| | 597 | print "Allocation passed:" |
| | 598 | PS_print(PS) |
| | 599 | print "Checking that entries are in order and correct level." |
| | 600 | good = True |
| | 601 | for i from 0 <= i < n-1: |
| | 602 | if not (PS.entries[i] == i and PS.levels[i] == n): |
| | 603 | print "Failed at i = %d!"%i |
| | 604 | print PS.entries[i], PS.levels[i], i, n |
| | 605 | good = False |
| | 606 | if not (PS.entries[n-1] == n-1 and PS.levels[n-1] == -1): |
| | 607 | print "Failed at i = %d!"%(n-1) |
| | 608 | good = False |
| | 609 | if not PS.degree == n or not PS.depth == 0: |
| | 610 | print "Incorrect degree or depth!" |
| | 611 | good = False |
| | 612 | if good: print "Everything seems in order, deallocating." |
| | 613 | PS_dealloc(PS) |
| | 614 | print "Deallocated." |
| | 615 | print "Creating PartitionStack from partition %s."%partition |
| | 616 | PS = PS_from_list(partition) |
| | 617 | print "PartitionStack's data:" |
| | 618 | print "entries -> %s"%[PS.entries[i] for i from 0 <= i < n] |
| | 619 | print "levels -> %s"%[PS.levels[i] for i from 0 <= i < n] |
| | 620 | print "depth = %d, degree = %d"%(PS.depth,PS.degree) |
| | 621 | PS_print(PS) |
| | 622 | print "Checking PS_is_discrete:" |
| | 623 | print "True" if PS_is_discrete(PS) else "False" |
| | 624 | print "Checking PS_num_cells:" |
| | 625 | print PS_num_cells(PS) |
| | 626 | print "Checking PS_is_mcr, min cell reps are:" |
| | 627 | L = [PS.entries[i] for i from 0 <= i < n if PS_is_mcr(PS, i)] |
| | 628 | print L |
| | 629 | print "Checking PS_is_fixed, fixed elements are:" |
| | 630 | print [PS.entries[l] for l in L if PS_is_fixed(PS, l)] |
| | 631 | print "Copying PartitionStack:" |
| | 632 | PS2 = PS_copy(PS) |
| | 633 | PS_print(PS2) |
| | 634 | print "Checking for consistency." |
| | 635 | good = True |
| | 636 | for i from 0 <= i < n: |
| | 637 | if PS.entries[i] != PS2.entries[i] or PS.levels[i] != PS2.levels[i]: |
| | 638 | print "Failed at i = %d!"%i |
| | 639 | good = False |
| | 640 | if PS.degree != PS2.degree or PS.depth != PS2.depth: |
| | 641 | print "Failure with degree or depth!" |
| | 642 | good = False |
| | 643 | if good: |
| | 644 | print "Everything is consistent." |
| | 645 | print "Clearing copy:" |
| | 646 | PS_clear(PS2) |
| | 647 | PS_print(PS2) |
| | 648 | for s in splits: |
| | 649 | print "Splitting point %d from original:"%s |
| | 650 | print PS_split_point(PS, s) |
| | 651 | PS_print(PS) |
| | 652 | print "Getting permutation from PS2->PS:" |
| | 653 | gamma = <int *> sage_malloc(n * sizeof(int)) |
| | 654 | PS_get_perm_from(PS, PS2, gamma) |
| | 655 | print [gamma[i] for i from 0 <= i < n] |
| | 656 | sage_free(gamma) |
| | 657 | print "Finding first smallest:" |
| | 658 | bitset_init(b, n) |
| | 659 | i = PS_first_smallest(PS, b) |
| | 660 | print "Minimal element is %d, bitset is:"%i |
| | 661 | print bitset_string(b) |
| | 662 | bitset_clear(b) |
| | 663 | print "Deallocating PartitionStacks." |
| | 664 | PS_dealloc(PS) |
| | 665 | PS_dealloc(PS2) |
| | 666 | print "Done." |
| | 667 | |
| | 668 | # Functions |
| | 669 | |
| | 670 | cdef inline int split_point_and_refine(PartitionStack *PS, int v, object S, |
| | 671 | int (*refine_and_return_invariant)\ |
| | 672 | (PartitionStack *PS, object S, int *cells_to_refine_by, int ctrb_len), |
| | 673 | int *cells_to_refine_by): |
| | 674 | """ |
| | 675 | Make the partition stack one longer by copying the last partition in the |
| | 676 | stack, split off a given point, and refine. Return the invariant given by |
| | 677 | the refinement function. |
| | 678 | |
| | 679 | INPUT: |
| | 680 | PS -- the partition stack to refine |
| | 681 | v -- the point to split |
| | 682 | S -- the structure |
| | 683 | refine_and_return_invariant -- the refinement function provided |
| | 684 | cells_to_refine_by -- an array, contents ignored |
| | 685 | |
| | 686 | """ |
| | 687 | PS.depth += 1 |
| | 688 | PS_clear(PS) |
| | 689 | cells_to_refine_by[0] = PS_split_point(PS, v) |
| | 690 | return refine_and_return_invariant(PS, S, cells_to_refine_by, 1) |
| | 691 | |
| | 692 | cdef bint all_children_are_equivalent_trivial(PartitionStack *PS, object S): |
| | 693 | return 0 |
| | 694 | |
| | 695 | cdef int refine_and_return_invariant_trivial(PartitionStack *PS, object S, int *cells_to_refine_by, int ctrb_len): |
| | 696 | return 0 |
| | 697 | |
| | 698 | cdef int compare_structures_trivial(int *gamma_1, int *gamma_2, object S): |
| | 699 | return 0 |
| | 700 | |
| | 701 | def test_get_aut_gp_and_can_lab_trivially(int n=6, partition=[[0,1,2],[3,4],[5]], |
| | 702 | canonical_label=True, base=False): |
| | 703 | """ |
| | 704 | sage: tttt = sage.groups.perm_gps.partn_ref.automorphism_group_canonical_label.test_get_aut_gp_and_can_lab_trivially |
| | 705 | sage: tttt() |
| | 706 | 12 |
| | 707 | sage: tttt(canonical_label=False, base=False) |
| | 708 | 12 |
| | 709 | sage: tttt(canonical_label=False, base=True) |
| | 710 | 12 |
| | 711 | sage: tttt(canonical_label=True, base=True) |
| | 712 | 12 |
| | 713 | sage: tttt(n=0, partition=[]) |
| | 714 | 1 |
| | 715 | sage: tttt(n=0, partition=[], canonical_label=False, base=False) |
| | 716 | 1 |
| | 717 | sage: tttt(n=0, partition=[], canonical_label=False, base=True) |
| | 718 | 1 |
| | 719 | sage: tttt(n=0, partition=[], canonical_label=True, base=True) |
| | 720 | 1 |
| | 721 | |
| | 722 | """ |
| | 723 | cdef int i, j |
| | 724 | cdef aut_gp_and_can_lab_return *output |
| | 725 | cdef Integer I = Integer(0) |
| | 726 | cdef int **part = <int **> sage_malloc((len(partition)+1) * sizeof(int *)) |
| | 727 | for i from 0 <= i < len(partition): |
| | 728 | part[i] = <int *> sage_malloc((len(partition[i])+1) * sizeof(int)) |
| | 729 | for j from 0 <= j < len(partition[i]): |
| | 730 | part[i][j] = partition[i][j] |
| | 731 | part[i][len(partition[i])] = -1 |
| | 732 | part[len(partition)] = NULL |
| | 733 | output = get_aut_gp_and_can_lab([], part, n, &all_children_are_equivalent_trivial, &refine_and_return_invariant_trivial, &compare_structures_trivial, canonical_label, base, True) |
| | 734 | mpz_set(I.value, output.order) |
| | 735 | print I |
| | 736 | for i from 0 <= i < len(partition): |
| | 737 | sage_free(part[i]) |
| | 738 | sage_free(part) |
| | 739 | mpz_clear(output.order) |
| | 740 | sage_free(output.generators) |
| | 741 | if base: |
| | 742 | sage_free(output.base) |
| | 743 | if canonical_label: |
| | 744 | sage_free(output.relabeling) |
| | 745 | sage_free(output) |
| | 746 | |
| | 747 | cdef aut_gp_and_can_lab_return *get_aut_gp_and_can_lab(object S, int **partition, int n, |
| | 748 | bint (*all_children_are_equivalent)(PartitionStack *PS, object S), |
| | 749 | int (*refine_and_return_invariant)\ |
| | 750 | (PartitionStack *PS, object S, int *cells_to_refine_by, int ctrb_len), |
| | 751 | int (*compare_structures)(int *gamma_1, int *gamma_2, object S), |
| | 752 | bint canonical_label, bint base, bint order): |
| | 753 | """ |
| | 754 | Traverse the search space for subgroup/canonical label calculation. |
| | 755 | |
| | 756 | INPUT: |
| | 757 | S -- pointer to the structure |
| | 758 | partition -- array representing a partition of the points |
| | 759 | len_partition -- length of the partition |
| | 760 | n -- the number of points (points are assumed to be 0,1,...,n-1) |
| | 761 | canonical_label -- whether to search for canonical label - if True, return |
| | 762 | the permutation taking S to its canonical label |
| | 763 | base -- if True, return the base for which the given generating set is a |
| | 764 | strong generating set |
| | 765 | order -- if True, return the order of the automorphism group |
| | 766 | all_children_are_equivalent -- pointer to a function |
| | 767 | INPUT: |
| | 768 | PS -- pointer to a partition stack |
| | 769 | S -- pointer to the structure |
| | 770 | OUTPUT: |
| | 771 | bint -- returns True if it can be determined that all refinements below |
| | 772 | the current one will result in an equivalent discrete partition |
| | 773 | refine_and_return_invariant -- pointer to a function |
| | 774 | INPUT: |
| | 775 | PS -- pointer to a partition stack |
| | 776 | S -- pointer to the structure |
| | 777 | alpha -- an array consisting of numbers, which indicate the starting |
| | 778 | positions of the cells to refine against (will likely be modified) |
| | 779 | OUTPUT: |
| | 780 | int -- returns an invariant under application of arbitrary permutations |
| | 781 | compare_structures -- pointer to a function |
| | 782 | INPUT: |
| | 783 | gamma_1, gamma_2 -- (list) permutations of the points of S |
| | 784 | S -- pointer to the structure |
| | 785 | OUTPUT: |
| | 786 | int -- 0 if gamma_1(S) = gamma_2(S), otherwise -1 or 1 (see docs for cmp), |
| | 787 | such that the set of all structures is well-ordered |
| | 788 | OUTPUT: |
| | 789 | pointer to a aut_gp_and_can_lab_return struct |
| | 790 | |
| | 791 | """ |
| | 792 | cdef PartitionStack *current_ps, *first_ps, *label_ps |
| | 793 | cdef int first_meets_current = -1 |
| | 794 | cdef int label_meets_current |
| | 795 | cdef int current_kids_are_same = 1 |
| | 796 | cdef int first_kids_are_same |
| | 797 | |
| | 798 | cdef int *current_indicators, *first_indicators, *label_indicators |
| | 799 | cdef int first_and_current_indicator_same |
| | 800 | cdef int label_and_current_indicator_same = -1 |
| | 801 | cdef int compared_current_and_label_indicators |
| | 802 | |
| | 803 | cdef OrbitPartition *orbits_of_subgroup |
| | 804 | cdef mpz_t subgroup_size |
| | 805 | cdef int subgroup_primary_orbit_size = 0 |
| | 806 | cdef int minimal_in_primary_orbit |
| | 807 | cdef int size_of_generator_array = 2*n*n |
| | 808 | |
| | 809 | cdef bitset_t *fixed_points_of_generators # i.e. fp |
| | 810 | cdef bitset_t *minimal_cell_reps_of_generators # i.e. mcr |
| | 811 | cdef int len_of_fp_and_mcr = 100 |
| | 812 | cdef int index_in_fp_and_mcr = -1 |
| | 813 | |
| | 814 | cdef bitset_t *vertices_to_split |
| | 815 | cdef bitset_t vertices_have_been_reduced |
| | 816 | cdef int *permutation, *id_perm, *cells_to_refine_by |
| | 817 | cdef int *vertices_determining_current_stack |
| | 818 | |
| | 819 | cdef int i, j, k |
| | 820 | cdef bint discrete, automorphism, update_label |
| | 821 | cdef bint backtrack, new_vertex, narrow, mem_err = 0 |
| | 822 | |
| | 823 | cdef aut_gp_and_can_lab_return *output = <aut_gp_and_can_lab_return *> sage_malloc(sizeof(aut_gp_and_can_lab_return)) |
| | 824 | if output is NULL: |
| | 825 | raise MemoryError |
| | 826 | |
| | 827 | if n == 0: |
| | 828 | output.generators = NULL |
| | 829 | output.num_gens = 0 |
| | 830 | output.relabeling = NULL |
| | 831 | output.base = NULL |
| | 832 | output.base_size = 0 |
| | 833 | mpz_init_set_si(output.order, 1) |
| | 834 | return output |
| | 835 | |
| | 836 | # Allocate: |
| | 837 | mpz_init_set_si(subgroup_size, 1) |
| | 838 | |
| | 839 | output.generators = <int *> sage_malloc( size_of_generator_array * sizeof(int) ) |
| | 840 | output.num_gens = 0 |
| | 841 | if base: |
| | 842 | output.base = <int *> sage_malloc( n * sizeof(int) ) |
| | 843 | output.base_size = 0 |
| | 844 | |
| | 845 | current_indicators = <int *> sage_malloc( n * sizeof(int) ) |
| | 846 | first_indicators = <int *> sage_malloc( n * sizeof(int) ) |
| | 847 | |
| | 848 | if canonical_label: |
| | 849 | label_indicators = <int *> sage_malloc( n * sizeof(int) ) |
| | 850 | output.relabeling = <int *> sage_malloc( n * sizeof(int) ) |
| | 851 | |
| | 852 | fixed_points_of_generators = <bitset_t *> sage_malloc( len_of_fp_and_mcr * sizeof(bitset_t) ) |
| | 853 | minimal_cell_reps_of_generators = <bitset_t *> sage_malloc( len_of_fp_and_mcr * sizeof(bitset_t) ) |
| | 854 | |
| | 855 | vertices_to_split = <bitset_t *> sage_malloc( n * sizeof(bitset_t) ) |
| | 856 | permutation = <int *> sage_malloc( n * sizeof(int) ) |
| | 857 | id_perm = <int *> sage_malloc( n * sizeof(int) ) |
| | 858 | cells_to_refine_by = <int *> sage_malloc( n * sizeof(int) ) |
| | 859 | vertices_determining_current_stack = <int *> sage_malloc( n * sizeof(int) ) |
| | 860 | |
| | 861 | current_ps = PS_new(n, 0) |
| | 862 | orbits_of_subgroup = OP_new(n) |
| | 863 | |
| | 864 | # Check for allocation failures: |
| | 865 | cdef bint succeeded = 1 |
| | 866 | if canonical_label: |
| | 867 | if label_indicators is NULL or output.relabeling is NULL: |
| | 868 | succeeded = 0 |
| | 869 | if label_indicators is not NULL: |
| | 870 | sage_free(label_indicators) |
| | 871 | if output.relabeling is not NULL: |
| | 872 | sage_free(output.relabeling) |
| | 873 | if base: |
| | 874 | if output.base is NULL: |
| | 875 | succeeded = 0 |
| | 876 | if not succeeded or \ |
| | 877 | output.generators is NULL or \ |
| | 878 | current_indicators is NULL or \ |
| | 879 | first_indicators is NULL or \ |
| | 880 | fixed_points_of_generators is NULL or \ |
| | 881 | minimal_cell_reps_of_generators is NULL or \ |
| | 882 | vertices_to_split is NULL or \ |
| | 883 | permutation is NULL or \ |
| | 884 | id_perm is NULL or \ |
| | 885 | cells_to_refine_by is NULL or \ |
| | 886 | vertices_determining_current_stack is NULL or \ |
| | 887 | current_ps is NULL or \ |
| | 888 | orbits_of_subgroup is NULL: |
| | 889 | if output.generators is not NULL: |
| | 890 | sage_free(output.generators) |
| | 891 | if base: |
| | 892 | if output.base is not NULL: |
| | 893 | sage_free(output.base) |
| | 894 | if current_indicators is not NULL: |
| | 895 | sage_free(current_indicators) |
| | 896 | if first_indicators is not NULL: |
| | 897 | sage_free(first_indicators) |
| | 898 | if fixed_points_of_generators is not NULL: |
| | 899 | sage_free(fixed_points_of_generators) |
| | 900 | if minimal_cell_reps_of_generators is not NULL: |
| | 901 | sage_free(minimal_cell_reps_of_generators) |
| | 902 | if vertices_to_split is not NULL: |
| | 903 | sage_free(vertices_to_split) |
| | 904 | if permutation is not NULL: |
| | 905 | sage_free(permutation) |
| | 906 | if id_perm is not NULL: |
| | 907 | sage_free(id_perm) |
| | 908 | if cells_to_refine_by is not NULL: |
| | 909 | sage_free(cells_to_refine_by) |
| | 910 | if vertices_determining_current_stack is not NULL: |
| | 911 | sage_free(vertices_determining_current_stack) |
| | 912 | if current_ps is not NULL: |
| | 913 | PS_dealloc(current_ps) |
| | 914 | if orbits_of_subgroup is not NULL: |
| | 915 | OP_dealloc(orbits_of_subgroup) |
| | 916 | sage_free(output) |
| | 917 | mpz_clear(subgroup_size) |
| | 918 | raise MemoryError |
| | 919 | |
| | 920 | # Initialize bitsets, checking for allocation failures: |
| | 921 | succeeded = 1 |
| | 922 | for i from 0 <= i < len_of_fp_and_mcr: |
| | 923 | try: |
| | 924 | bitset_init(fixed_points_of_generators[i], n) |
| | 925 | except MemoryError: |
| | 926 | succeeded = 0 |
| | 927 | for j from 0 <= j < i: |
| | 928 | bitset_clear(fixed_points_of_generators[j]) |
| | 929 | bitset_clear(minimal_cell_reps_of_generators[j]) |
| | 930 | break |
| | 931 | try: |
| | 932 | bitset_init(minimal_cell_reps_of_generators[i], n) |
| | 933 | except MemoryError: |
| | 934 | succeeded = 0 |
| | 935 | for j from 0 <= j < i: |
| | 936 | bitset_clear(fixed_points_of_generators[j]) |
| | 937 | bitset_clear(minimal_cell_reps_of_generators[j]) |
| | 938 | bitset_clear(fixed_points_of_generators[i]) |
| | 939 | break |
| | 940 | if succeeded: |
| | 941 | for i from 0 <= i < n: |
| | 942 | try: |
| | 943 | bitset_init(vertices_to_split[i], n) |
| | 944 | except MemoryError: |
| | 945 | succeeded = 0 |
| | 946 | for j from 0 <= j < i: |
| | 947 | bitset_clear(vertices_to_split[j]) |
| | 948 | for j from 0 <= j < len_of_fp_and_mcr: |
| | 949 | bitset_clear(fixed_points_of_generators[j]) |
| | 950 | bitset_clear(minimal_cell_reps_of_generators[j]) |
| | 951 | break |
| | 952 | if succeeded: |
| | 953 | try: |
| | 954 | bitset_init(vertices_have_been_reduced, n) |
| | 955 | except MemoryError: |
| | 956 | succeeded = 0 |
| | 957 | for j from 0 <= j < n: |
| | 958 | bitset_clear(vertices_to_split[j]) |
| | 959 | for j from 0 <= j < len_of_fp_and_mcr: |
| | 960 | bitset_clear(fixed_points_of_generators[j]) |
| | 961 | bitset_clear(minimal_cell_reps_of_generators[j]) |
| | 962 | if not succeeded: |
| | 963 | sage_free(output.generators) |
| | 964 | if base: |
| | 965 | sage_free(output.base) |
| | 966 | sage_free(current_indicators) |
| | 967 | sage_free(first_indicators) |
| | 968 | if canonical_label: |
| | 969 | sage_free(output.relabeling) |
| | 970 | sage_free(label_indicators) |
| | 971 | sage_free(fixed_points_of_generators) |
| | 972 | sage_free(minimal_cell_reps_of_generators) |
| | 973 | sage_free(vertices_to_split) |
| | 974 | sage_free(permutation) |
| | 975 | sage_free(id_perm) |
| | 976 | sage_free(cells_to_refine_by) |
| | 977 | sage_free(vertices_determining_current_stack) |
| | 978 | PS_dealloc(current_ps) |
| | 979 | sage_free(output) |
| | 980 | mpz_clear(subgroup_size) |
| | 981 | raise MemoryError |
| | 982 | |
| | 983 | bitset_zero(vertices_have_been_reduced) |
| | 984 | |
| | 985 | # Copy data of partition to current_ps. |
| | 986 | i = 0 |
| | 987 | j = 0 |
| | 988 | while partition[i] is not NULL: |
| | 989 | k = 0 |
| | 990 | while partition[i][k] != -1: |
| | 991 | current_ps.entries[j+k] = partition[i][k] |
| | 992 | current_ps.levels[j+k] = n |
| | 993 | k += 1 |
| | 994 | current_ps.levels[j+k-1] = 0 |
| | 995 | PS_move_min_to_front(current_ps, j, j+k-1) |
| | 996 | j += k |
| | 997 | i += 1 |
| | 998 | current_ps.levels[j-1] = -1 |
| | 999 | current_ps.depth = 0 |
| | 1000 | current_ps.degree = n |
| | 1001 | |
| | 1002 | # default values of "infinity" |
| | 1003 | for i from 0 <= i < n: |
| | 1004 | first_indicators[i] = -1 |
| | 1005 | if canonical_label: |
| | 1006 | for i from 0 <= i < n: |
| | 1007 | label_indicators[i] = -1 |
| | 1008 | |
| | 1009 | # set up the identity permutation |
| | 1010 | for i from 0 <= i < n: |
| | 1011 | id_perm[i] = i |
| | 1012 | |
| | 1013 | # Our first refinement needs to check every cell of the partition, |
| | 1014 | # so cells_to_refine_by needs to be a list of pointers to each cell. |
| | 1015 | j = 1 |
| | 1016 | cells_to_refine_by[0] = 0 |
| | 1017 | for i from 0 < i < n: |
| | 1018 | if current_ps.levels[i-1] == 0: |
| | 1019 | cells_to_refine_by[j] = i |
| | 1020 | j += 1 |
| | 1021 | # Ignore the invariant this time, since we are |
| | 1022 | # creating the root of the search tree. |
| | 1023 | refine_and_return_invariant(current_ps, S, cells_to_refine_by, j) |
| | 1024 | |
| | 1025 | # Refine down to a discrete partition |
| | 1026 | while not PS_is_discrete(current_ps): |
| | 1027 | if not all_children_are_equivalent(current_ps, S): |
| | 1028 | current_kids_are_same = current_ps.depth + 1 |
| | 1029 | vertices_determining_current_stack[current_ps.depth] = PS_first_smallest(current_ps, vertices_to_split[current_ps.depth]) |
| | 1030 | bitset_unset(vertices_have_been_reduced, current_ps.depth) |
| | 1031 | i = current_ps.depth |
| | 1032 | current_indicators[i] = split_point_and_refine(current_ps, vertices_determining_current_stack[i], S, refine_and_return_invariant, cells_to_refine_by) |
| | 1033 | first_indicators[i] = current_indicators[i] |
| | 1034 | if canonical_label: |
| | 1035 | label_indicators[i] = current_indicators[i] |
| | 1036 | if base: |
| | 1037 | output.base[i] = vertices_determining_current_stack[i] |
| | 1038 | output.base_size += 1 |
| | 1039 | |
| | 1040 | first_meets_current = current_ps.depth |
| | 1041 | first_kids_are_same = current_ps.depth |
| | 1042 | first_and_current_indicator_same = current_ps.depth |
| | 1043 | first_ps = PS_copy(current_ps) |
| | 1044 | if canonical_label: |
| | 1045 | label_meets_current = current_ps.depth |
| | 1046 | label_and_current_indicator_same = current_ps.depth |
| | 1047 | compared_current_and_label_indicators = 0 |
| | 1048 | label_ps = PS_copy(current_ps) |
| | 1049 | current_ps.depth -= 1 |
| | 1050 | |
| | 1051 | # Main loop: |
| | 1052 | while current_ps.depth != -1: |
| | 1053 | |
| | 1054 | # If necessary, update label_meets_current |
| | 1055 | if canonical_label and label_meets_current > current_ps.depth: |
| | 1056 | label_meets_current = current_ps.depth |
| | 1057 | |
| | 1058 | # I. Search for a new vertex to split, and update subgroup information |
| | 1059 | new_vertex = 0 |
| | 1060 | if current_ps.depth > first_meets_current: |
| | 1061 | # If we are not at a node of the first stack, reduce size of |
| | 1062 | # vertices_to_split by using the symmetries we already know. |
| | 1063 | if not bitset_check(vertices_have_been_reduced, current_ps.depth): |
| | 1064 | for i from 0 <= i <= index_in_fp_and_mcr: |
| | 1065 | j = 0 |
| | 1066 | while j < current_ps.depth and bitset_check(fixed_points_of_generators[i], vertices_determining_current_stack[j]): |
| | 1067 | j += 1 |
| | 1068 | # If each vertex split so far is fixed by generator i, |
| | 1069 | # then remove elements of vertices_to_split which are |
| | 1070 | # not minimal in their orbits under generator i. |
| | 1071 | if j == current_ps.depth: |
| | 1072 | for k from 0 <= k < n: |
| | 1073 | if bitset_check(vertices_to_split[current_ps.depth], k) and not bitset_check(minimal_cell_reps_of_generators[i], k): |
| | 1074 | bitset_flip(vertices_to_split[current_ps.depth], k) |
| | 1075 | bitset_flip(vertices_have_been_reduced, current_ps.depth) |
| | 1076 | # Look for a new point to split. |
| | 1077 | i = vertices_determining_current_stack[current_ps.depth] + 1 |
| | 1078 | while i < n and not bitset_check(vertices_to_split[current_ps.depth], i): |
| | 1079 | i += 1 |
| | 1080 | if i < n: |
| | 1081 | # There is a new point. |
| | 1082 | vertices_determining_current_stack[current_ps.depth] = i |
| | 1083 | new_vertex = 1 |
| | 1084 | else: |
| | 1085 | # No new point: backtrack. |
| | 1086 | current_ps.depth -= 1 |
| | 1087 | else: |
| | 1088 | # If we are at a node of the first stack, the above reduction |
| | 1089 | # will not help. Also, we must update information about |
| | 1090 | # primary orbits here. |
| | 1091 | if current_ps.depth < first_meets_current: |
| | 1092 | # If we are done searching under this part of the first |
| | 1093 | # stack, then first_meets_current is one higher, and we |
| | 1094 | # are looking at a new primary orbit (corresponding to a |
| | 1095 | # larger subgroup in the stabilizer chain). |
| | 1096 | first_meets_current = current_ps.depth |
| | 1097 | for i from 0 <= i < n: |
| | 1098 | if bitset_check(vertices_to_split[current_ps.depth], i): |
| | 1099 | minimal_in_primary_orbit = i |
| | 1100 | break |
| | 1101 | while 1: |
| | 1102 | i = vertices_determining_current_stack[current_ps.depth] |
| | 1103 | # This was the last point to be split here. |
| | 1104 | # If it is in the same orbit as minimal_in_primary_orbit, |
| | 1105 | # then count it as an element of the primary orbit. |
| | 1106 | if OP_find(orbits_of_subgroup, i) == OP_find(orbits_of_subgroup, minimal_in_primary_orbit): |
| | 1107 | subgroup_primary_orbit_size += 1 |
| | 1108 | # Look for a new point to split. |
| | 1109 | i += 1 |
| | 1110 | while i < n and not bitset_check(vertices_to_split[current_ps.depth], i): |
| | 1111 | i += 1 |
| | 1112 | if i < n: |
| | 1113 | # There is a new point. |
| | 1114 | vertices_determining_current_stack[current_ps.depth] = i |
| | 1115 | if orbits_of_subgroup.mcr[OP_find(orbits_of_subgroup, i)] == i: |
| | 1116 | new_vertex = 1 |
| | 1117 | break |
| | 1118 | else: |
| | 1119 | # No new point: backtrack. |
| | 1120 | # Note that now, we are backtracking up the first stack. |
| | 1121 | vertices_determining_current_stack[current_ps.depth] = -1 |
| | 1122 | # If every choice of point to split gave something in the |
| | 1123 | # (same!) primary orbit, then all children of the first |
| | 1124 | # stack at this point are equivalent. |
| | 1125 | j = 0 |
| | 1126 | for i from 0 <= i < n: |
| | 1127 | if bitset_check(vertices_to_split[current_ps.depth], i): |
| | 1128 | j += 1 |
| | 1129 | if j == subgroup_primary_orbit_size and first_kids_are_same == current_ps.depth+1: |
| | 1130 | first_kids_are_same = current_ps.depth |
| | 1131 | # Update group size and backtrack. |
| | 1132 | mpz_mul_si(subgroup_size, subgroup_size, subgroup_primary_orbit_size) |
| | 1133 | subgroup_primary_orbit_size = 0 |
| | 1134 | current_ps.depth -= 1 |
| | 1135 | break |
| | 1136 | if not new_vertex: |
| | 1137 | continue |
| | 1138 | |
| | 1139 | if current_kids_are_same > current_ps.depth + 1: |
| | 1140 | current_kids_are_same = current_ps.depth + 1 |
| | 1141 | if first_and_current_indicator_same > current_ps.depth: |
| | 1142 | first_and_current_indicator_same = current_ps.depth |
| | 1143 | if canonical_label and label_and_current_indicator_same > current_ps.depth: |
| | 1144 | label_and_current_indicator_same = current_ps.depth |
| | 1145 | compared_current_and_label_indicators = 0 |
| | 1146 | |
| | 1147 | # II. Refine down to a discrete partition, or until |
| | 1148 | # we leave the part of the tree we are interested in |
| | 1149 | discrete = 0 |
| | 1150 | while 1: |
| | 1151 | i = current_ps.depth |
| | 1152 | current_indicators[i] = split_point_and_refine(current_ps, vertices_determining_current_stack[i], S, refine_and_return_invariant, cells_to_refine_by) |
| | 1153 | if first_and_current_indicator_same == i and current_indicators[i] == first_indicators[i]: |
| | 1154 | first_and_current_indicator_same += 1 |
| | 1155 | if canonical_label: |
| | 1156 | if compared_current_and_label_indicators == 0: |
| | 1157 | if label_indicators[i] == -1: |
| | 1158 | compared_current_and_label_indicators = -1 |
| | 1159 | else: |
| | 1160 | compared_current_and_label_indicators = cmp(current_indicators[i], label_indicators[i]) |
| | 1161 | if label_and_current_indicator_same == i and compared_current_and_label_indicators == 0: |
| | 1162 | label_and_current_indicator_same += 1 |
| | 1163 | if compared_current_and_label_indicators > 0: |
| | 1164 | label_indicators[i] = current_indicators[i] |
| | 1165 | if first_and_current_indicator_same < current_ps.depth and (not canonical_label or compared_current_and_label_indicators < 0): |
| | 1166 | break |
| | 1167 | if PS_is_discrete(current_ps): |
| | 1168 | discrete = 1 |
| | 1169 | break |
| | 1170 | vertices_determining_current_stack[current_ps.depth] = PS_first_smallest(current_ps, vertices_to_split[current_ps.depth]) |
| | 1171 | bitset_unset(vertices_have_been_reduced, current_ps.depth) |
| | 1172 | if not all_children_are_equivalent(current_ps, S): |
| | 1173 | current_kids_are_same = current_ps.depth + 1 |
| | 1174 | |
| | 1175 | # III. Check for automorphisms and labels |
| | 1176 | automorphism = 0 |
| | 1177 | backtrack = 1 - discrete |
| | 1178 | if discrete: |
| | 1179 | if current_ps.depth == first_and_current_indicator_same: |
| | 1180 | PS_get_perm_from(current_ps, first_ps, permutation) |
| | 1181 | if compare_structures(permutation, id_perm, S) == 0: |
| | 1182 | automorphism = 1 |
| | 1183 | if not automorphism: |
| | 1184 | if (not canonical_label) or (compared_current_and_label_indicators < 0): |
| | 1185 | backtrack = 1 |
| | 1186 | else: |
| | 1187 | # if we get here, discrete must be true |
| | 1188 | update_label = 0 |
| | 1189 | if (compared_current_and_label_indicators > 0) or (current_ps.depth < label_ps.depth): |
| | 1190 | update_label = 1 |
| | 1191 | else: |
| | 1192 | i = compare_structures(current_ps.entries, label_ps.entries, S) |
| | 1193 | if i > 0: |
| | 1194 | update_label = 1 |
| | 1195 | elif i < 0: |
| | 1196 | backtrack = 1 |
| | 1197 | else: |
| | 1198 | PS_get_perm_from(current_ps, label_ps, permutation) |
| | 1199 | automorphism = 1 |
| | 1200 | if update_label: |
| | 1201 | PS_copy_from_to(current_ps, label_ps) |
| | 1202 | compared_current_and_label_indicators = 0 |
| | 1203 | label_meets_current = current_ps.depth |
| | 1204 | label_and_current_indicator_same = current_ps.depth |
| | 1205 | label_indicators[current_ps.depth] = -1 |
| | 1206 | backtrack = 1 |
| | 1207 | if automorphism: |
| | 1208 | if index_in_fp_and_mcr < len_of_fp_and_mcr - 1: |
| | 1209 | index_in_fp_and_mcr += 1 |
| | 1210 | bitset_zero(fixed_points_of_generators[index_in_fp_and_mcr]) |
| | 1211 | bitset_zero(minimal_cell_reps_of_generators[index_in_fp_and_mcr]) |
| | 1212 | for i from 0 <= i < n: |
| | 1213 | if permutation[i] == i: |
| | 1214 | bitset_set(fixed_points_of_generators[index_in_fp_and_mcr], i) |
| | 1215 | bitset_set(minimal_cell_reps_of_generators[index_in_fp_and_mcr], i) |
| | 1216 | else: |
| | 1217 | bitset_unset(fixed_points_of_generators[index_in_fp_and_mcr], i) |
| | 1218 | k = i |
| | 1219 | j = permutation[i] |
| | 1220 | while j != i: |
| | 1221 | if j < k: k = j |
| | 1222 | j = permutation[j] |
| | 1223 | if k == i: |
| | 1224 | bitset_set(minimal_cell_reps_of_generators[index_in_fp_and_mcr], i) |
| | 1225 | else: |
| | 1226 | bitset_unset(minimal_cell_reps_of_generators[index_in_fp_and_mcr], i) |
| | 1227 | if OP_merge_list_perm(orbits_of_subgroup, permutation): # if permutation made orbits coarser |
| | 1228 | if n*output.num_gens == size_of_generator_array: |
| | 1229 | # must double its size |
| | 1230 | size_of_generator_array *= 2 |
| | 1231 | output.generators = <int *> sage_realloc( output.generators, size_of_generator_array ) |
| | 1232 | if output.generators is NULL: |
| | 1233 | mem_err = True |
| | 1234 | break # main loop |
| | 1235 | j = n*output.num_gens |
| | 1236 | for i from 0 <= i < n: |
| | 1237 | output.generators[j + i] = permutation[i] |
| | 1238 | output.num_gens += 1 |
| | 1239 | if orbits_of_subgroup.mcr[OP_find(orbits_of_subgroup, minimal_in_primary_orbit)] != minimal_in_primary_orbit: |
| | 1240 | current_ps.depth = first_meets_current |
| | 1241 | continue # main loop |
| | 1242 | if canonical_label: |
| | 1243 | current_ps.depth = label_meets_current |
| | 1244 | else: |
| | 1245 | current_ps.depth = first_meets_current |
| | 1246 | if bitset_check(vertices_have_been_reduced, current_ps.depth): |
| | 1247 | bitset_and(vertices_to_split[current_ps.depth], vertices_to_split[current_ps.depth], minimal_cell_reps_of_generators[index_in_fp_and_mcr]) |
| | 1248 | continue # main loop |
| | 1249 | if backtrack: |
| | 1250 | i = current_ps.depth |
| | 1251 | current_ps.depth = min(max(first_kids_are_same - 1, label_and_current_indicator_same), current_kids_are_same - 1) |
| | 1252 | if i == current_kids_are_same: |
| | 1253 | continue # main loop |
| | 1254 | if index_in_fp_and_mcr < len_of_fp_and_mcr - 1: |
| | 1255 | index_in_fp_and_mcr += 1 |
| | 1256 | bitset_zero(fixed_points_of_generators[index_in_fp_and_mcr]) |
| | 1257 | bitset_zero(minimal_cell_reps_of_generators[index_in_fp_and_mcr]) |
| | 1258 | j = current_ps.depth |
| | 1259 | current_ps.depth = i # just for mcr and fixed functions... |
| | 1260 | for i from 0 <= i < n: |
| | 1261 | if PS_is_mcr(current_ps, i): |
| | 1262 | bitset_set(minimal_cell_reps_of_generators[index_in_fp_and_mcr], i) |
| | 1263 | if PS_is_fixed(current_ps, i): |
| | 1264 | bitset_set(fixed_points_of_generators[index_in_fp_and_mcr], i) |
| | 1265 | current_ps.depth = j |
| | 1266 | if bitset_check(vertices_have_been_reduced, current_ps.depth): |
| | 1267 | bitset_and(vertices_to_split[current_ps.depth], vertices_to_split[current_ps.depth], minimal_cell_reps_of_generators[index_in_fp_and_mcr]) |
| | 1268 | |
| | 1269 | # End of main loop. |
| | 1270 | |
| | 1271 | mpz_init_set(output.order, subgroup_size) |
| | 1272 | if canonical_label: |
| | 1273 | for i from 0 <= i < n: |
| | 1274 | output.relabeling[label_ps.entries[i]] = i |
| | 1275 | |
| | 1276 | # Deallocate: |
| | 1277 | for i from 0 <= i < len_of_fp_and_mcr: |
| | 1278 | bitset_clear(fixed_points_of_generators[i]) |
| | 1279 | bitset_clear(minimal_cell_reps_of_generators[i]) |
| | 1280 | for i from 0 <= i < n: |
| | 1281 | bitset_clear(vertices_to_split[i]) |
| | 1282 | bitset_clear(vertices_have_been_reduced) |
| | 1283 | sage_free(current_indicators) |
| | 1284 | sage_free(first_indicators) |
| | 1285 | if canonical_label: |
| | 1286 | PS_dealloc(label_ps) |
| | 1287 | sage_free(label_indicators) |
| | 1288 | sage_free(fixed_points_of_generators) |
| | 1289 | sage_free(minimal_cell_reps_of_generators) |
| | 1290 | sage_free(vertices_to_split) |
| | 1291 | sage_free(permutation) |
| | 1292 | sage_free(id_perm) |
| | 1293 | sage_free(cells_to_refine_by) |
| | 1294 | sage_free(vertices_determining_current_stack) |
| | 1295 | PS_dealloc(current_ps) |
| | 1296 | PS_dealloc(first_ps) |
| | 1297 | OP_dealloc(orbits_of_subgroup) |
| | 1298 | mpz_clear(subgroup_size) |
| | 1299 | if not mem_err: |
| | 1300 | return output |
| | 1301 | else: |
| | 1302 | mpz_clear(output.order) |
| | 1303 | sage_free(output.generators) |
| | 1304 | if base: |
| | 1305 | sage_free(output.base) |
| | 1306 | if canonical_label: |
| | 1307 | sage_free(output.relabeling) |
| | 1308 | sage_free(output) |
| | 1309 | output = NULL |
| | 1310 | raise MemoryError |
| | 1311 | |
| | 1312 | |
| | 1313 | |
| | 1314 | |
| | 1315 | |
| | 1316 | |
| | 1317 | |
| | 1318 | |
| | 1319 | |
| | 1320 | |
| | 1321 | |
| | 1322 | |
| | 1323 | |
| | 1324 | |