Path: blob/master/src/sage/groups/perm_gps/partn_ref2/refinement_generic.pyx
8817 views
r"""1Automorphism groups and canonical labels.23For details see section 3 of [Feu13]_.45Definitions6###########78Let `G` be a group which acts on a finite set `X` and let `\mathcal{L}(G)`9denote the set of subgroups of `G`. We say that10a mapping `Can_G: X \rightarrow X \times G \times \mathcal{L}(G),11x \mapsto \left( CF_G(x), T_G(x), G_x \right)` with1213- `CF_G(gx) = CF_G(x)` for all `x \in X` and all `g\in G` (canonical form)14- `CF_G(x) = T_G(x) x` for all `x \in X` (transporter element)15- `G_x = \{g \in G \mid gx=x\}` (stabilizer)1617is a canonization algorithm.1819Let `H` be another group acting on a set `Y`. A pair `(\theta, \omega)` with20a homomorphism `\theta: G \rightarrow H` and `\omega: X \rightarrow Y`21is called a homomorphism of group actions if `\omega(gx) =22\theta(g)\omega(x)` for all `g \in G`, for all `x \in X`. In the case that23`G=H` and `\theta = id_G`, we call `\omega` a `G`-homomorphism.2425A standard26partition is a sequence `C = (C_1, ..., C_k)` of subsets of `[n] = \{0, \ldots,27n-1\}` where the `C_i` are28disjoint, consecutive intervals and their union is equal to `[n]`. Each element29is30called a cell and the elements lying in a cell of cardinality 1 are called31fixed points. Let `I_C` be a sequence of all fixed points of `C` (the exact32ordering is important, but will be determined by the algorithm).33The stabilizer of `(S_n)_C` is a standard Young subgroup of `S_n`.343536Requirements37############3839In the following we want to present an efficient algorithm for the canonization40problem in the case that the group action is of type `G \rtimes S_n` on41`X^n`. For this goal, we suppose that the group action of `G = G \rtimes \{id\}`42on `X^n` has the following properties:4344-- The action of `G \rtimes \{id\}` on `X^n` is given by a direct product of45(maybe different) group actions of `G` on `X`.4647-- For every `i \in \{0, \ldots, n-1\}` let `\Pi^{(i)}: X^n \rightarrow X`48be the projection to the `i`-th coordinate. The function `\Pi^{(i)}`49is an invariant under the action of the subgroup50`(S_n)_i = \{\pi \in S_n \mid \pi(i) = i\}`.5152Let `(i_0, \ldots, i_{k-1}) = I \subseteq \{0, \ldots, n-1\}` be an53injective sequence. We define54`\Pi^{(I)}(x) := (\Pi^{(i_0)}(x), \ldots, \Pi^{(i_{k-1})}(x))`.55We call an element `x \in X` `I`-semicanonical if and only if56`\Pi^{(I)}(x) \leq \Pi^{(I)}(gx)` for all `g \in G`.5758For each `x \in X^n` there is obviously an `I`-semicanonical element in the orbit `Gx`.59Suppose that `x` is already `I`-semicanonical60and `J` is an injective sequence which extends61`I` by one further coordinate `i`, then we call the process of computing62a `J`-semicanonical representative the inner minimization at position `i`.6364This could be described by a group action of the stabilizer `G_{\Pi^{(I)}(x)}`.65on `x_i`.666768Partitions and refinements69##########################7071Now the idea for the canonization algorithm for the action of `G \rtimes72S_n` on `X^n` can be formulated as follows.73Let us suppose that we want74to canonize `x\in X^n`. We build a75backtrack tree in the following manner:7677- each node gets represented by a quadruple `(C, I_C, y, G_{\Pi^{(I_C)}(y))})`78where `C` is an ordered partition, `I_C` a subsequence of its fixed79points, `y \in X^n` is `I_C`-semicanonical80and `G_{\Pi^{(I_C)}(y))}` is the remaining group of the inner action81- the root node is on level `0` and gets represented by `(([n]), (), x, G)`82- nodes with `| I_C | = n`, i.e. all coordinates are fixed, will be leafs83and we have `G_{\Pi^{(I_C)}(y))} = G_y`84- the children of some other node `(C, I_C, y, G_{\Pi^{(I_C)}(y))})` are85constructed by86* picking one cell `C_j` with `| C_j |` (the choice has to be invariant in87some sense)88* separating the point `\min(C_j)` from its cell `C_j` which leads to the partition89`D` and the sequence `I_D`90* building the `| C_j |` successors `(D, I_D, z_k, G_{\Pi^{(I_D)}(z_k))})`91by applying the permutation92`\sigma_k := (\min(C_j), k)` for all `k \in C_j` to `y` and computing an93`I_D`-semicanonical representative `z_k` of `\sigma_k y`94- if the projection `\Pi^{(I_D)}(z_k)` is not optimal then we stop the95backtracking in this node (i.e. prune the subtree below this node)96- the tree is traversed in a depth-first manner97- the smallest reached leaf node is defined to be the canonical form98(the defined ordering takes all comparisons in predecessors also into account)99- equal leaf nodes correspond to automorphism of `x`, they could be used to100define further pruning methods.101102We are able to speed up the computation by making use of refinements103(i.e. `(S_n)_C`-homomorphisms). Suppose we constructed a node104`(C, I_C, y, G_{\Pi^{(I_C)}(y))})`. We define105`Y := \{z \in X^n \mid \Pi^{(I_C)}(z) = \Pi^{(I_C)}(y)\}`106and we search for functions `\omega: Y \rightarrow \mathbb{Z}^n` which are107constant on the orbits of `G_{\Pi^{(I_C)}(y)}` and compatible with the action108of `(S_n)_C` on both sides. Then we compute a permutation `\pi \in (S_n)_C`109which cell-wisely sorts the entries of `\omega(y)`. Afterwards we are allowed110to reduce ourselves to the stabilizer `(S_n)_D` of `\omega(\pi y)`. If this111stabilizer contains further fixed points, they are appended (in some fixed112order) to the sequence `I_C` in order to define `I_D`. Furthermore the element113`y` gets updated by an `I_D`-semicanonical representative of this orbit. These114refinements could also be applied iteratively.115116As said before, the backtrack search tree is traversed in a depth-first search117manner, hence we maintain a candidate for the result. Each newly constructed118node is compared to this candidate. If it119- compares larger, then we can prune the120subtree rooted in this node121- compares smaller, then we replace the candidate by the actual node122(more precisely by the next leaf node we will construct, for this we123use the boolean flag ``_is_candidate_initialized``)124- compares equal we just continue.125126127Implementation Details128######################129130The class :class:`~sage.groups.perm_gps.partn_ref2.PartitionRefinement_generic`131provides a framework for such a backtracking.132It maintains the partition `C` and the sequence `I_C`.133Instead of permuting the elements `x \in X`134during the construction of nodes and in the refinements,135we use a ``PartitionStack``,136see :mod:`sage.groups.perm_gps.partn_ref.automorphism_group_canonical_label`.137Hence, `C = (C_1, ..., C_k)` and `I_C` will be maintained implicitly. If `\pi \in S_n` is the permutation138applied to this node, then we will store `\pi(I_C)` and in the partition stack139we will store `(\pi(C_1), ..., \pi(C_k))`.140141The class further decides when we have to142call the inner minimization and which subtrees could be pruned by the use143of automorphisms, see :class:`sage.groups.perm_gps.partn_ref2.LabelledBranching` for more details.144145Derived classes146###############147148Derived classes have to implement the following methods, see the149method descriptions for more information:150- Cython Functions:151- bint _inner_min_(self, int pos, bint * inner_group_changed)152- bint _refine(self, bint * part_changed)153- tuple _store_state_(self)154- void _restore_state_(self, tuple act_state)155- void _store_best_(self)156- void _latex_act_node(self, str comment=None) (to use the implemented debugging method157via printing the backtrack tree using latex)158- bint _minimization_allowed_on_col(self, int pos)159160- Python functions:161- get_canonical_form(self)162- get_transporter(self)163- get_autom_gens(self)164165AUTHORS:166167- Thomas Feulner (2012-11-15): initial version168169REFERENCES:170171.. [Feu13] Feulner, Thomas, "Eine kanonische Form172zur Darstellung aequivalenter Codes --173Computergestuetzte Berechnung und ihre Anwendung174in der Codierungstheorie, Kryptographie und Geometrie --",175Dissertation, University of Bayreuth, 2013.176"""177178#*****************************************************************************179# Copyright (C) 2012 Thomas Feulner <[email protected]>180#181# Distributed under the terms of the GNU General Public License (GPL)182# as published by the Free Software Foundation; either version 2 of183# the License, or (at your option) any later version.184# http://www.gnu.org/licenses/185#*****************************************************************************186187include 'sage/groups/perm_gps/partn_ref/data_structures_pyx.pxi'188189from copy import copy190191cdef tuple PS_refinement(PartitionStack * part, long *refine_vals, long *best,192int begin, int end,193bint * cand_initialized, bint *changed_partition):194"""195Refine the partition stack by the values given by ``refine_vals``.196We also compare our actual refinement result with the vector ``best`` in the197case that ``cand_initialized`` is set to ``True``.198199If the current (canonical) vector is smaller than ``best``, then we will200replace the values stored in ``best`` and set ``cand_initialized``201to ``False``.202203The function will set ``changed_partition`` to ``True`` if and only if204some cell was refined.205206The function will return the boolean value ``False`` if and only if207the (canonical) vector is larger than ``best``. The second entry of the208tuple will contain all new singleton cells, which appeared in the209refinement of ``refine_vals``.210"""211global global_refine_vals_array212213changed_partition[0] = False214cdef int i = begin, loc_begin = begin, j, last_val, act_val215cdef list newly_fixed = []216217while i < end:218if part.levels[i] <= part.depth:219# [loc_begin, ..., i] is a block of the old partition220if i > loc_begin:221global_refine_vals_array = refine_vals;222qsort(part.entries + loc_begin, (i+1)-loc_begin, sizeof(int), my_comp_func);223last_val = refine_vals[ part.entries[loc_begin] ]224j = loc_begin225while 1:226act_val = refine_vals[ part.entries[j] ]227if act_val != last_val:228if j == 1 or part.levels[j - 2] <= part.depth:229# a block of size 1230newly_fixed.append(part.entries[j - 1])231232part.levels[j - 1] = part.depth # a new block233last_val = act_val234changed_partition[0] = True235236# compare with the candidate237if cand_initialized[0]:238if act_val < best[j]:239cand_initialized[0] = False240best[j] = act_val241if act_val > best[j]:242return (False, [])243else:244best[j] = act_val245246if j == i: # check for end of while247if loc_begin != i and part.levels[i - 1] <= part.depth:248# this is a singleton249newly_fixed.append(part.entries[i])250break251252j += 1253254255loc_begin = i + 1256i += 1257return (True, newly_fixed)258259cdef class _BestValStore:260r"""261This class implements a dynamic array of integer vectors of length `n`.262"""263def __cinit__(self, int n):264r"""265Initialization.266267EXAMPLES::268269sage: from sage.groups.perm_gps.partn_ref2.refinement_generic import _BestValStore270sage: B = _BestValStore(5)271"""272self.default_data_length = n273self.storage_length = 0274self.values = <long *> sage_malloc(0)275if self.values is NULL:276raise MemoryError('allocating _BestValStore')277278def __dealloc__(self):279"""280Dealloc.281"""282sage_free(self.values)283284cdef long * get_row(self, int i):285r"""286Return the i-th row.287288If this row is not yet initialized (i >= self.storage_length), then289we extend the array and fill the new positions290with the all zero vector.291"""292if i >= self.storage_length:293self.values = < long *> sage_realloc(self.values, (i + 1)* self.default_data_length * sizeof(long))294if self.values is NULL:295raise MemoryError('resizing _BestValStore')296self.storage_length = i + 1297return self.values+(i*self.default_data_length)298299cdef class LabelledBranching:300r"""301This class implements complete labelled branchings.302303To each subgroup of `S_n` we can uniquely assign a directed forest304on `n` vertices, where the305edges `(i,j)` fulfill `i<j` and some further conditions on the edge labels306(which we do not want to state).307This graph is called a complete labelled branching.308309The edges `(i,j)` will be stored in a vector ``father`` with310``father_j = -1`` if `j` is a root of a tree, and311``father_j = i`` if `i` is the predecessor of `j`, i.e. is an `(i,j)` is an edge.312313EXAMPLES::314315sage: from sage.groups.perm_gps.partn_ref2.refinement_generic import LabelledBranching316sage: L = LabelledBranching(3)317sage: L.add_gen(libgap.eval('(1,2,3)'))318sage: L.get_order()3193320sage: L.small_generating_set()321[(1,2,3)]322"""323324def __cinit__(self, n):325r"""326Initialization by the identity subgroup.327328EXAMPLES::329330sage: from sage.groups.perm_gps.partn_ref2.refinement_generic import LabelledBranching331sage: L = LabelledBranching(3)332"""333from sage.libs.gap.libgap import libgap334335self.n = n336self.group = libgap.eval("Group(())")337self.ClosureGroup = libgap.eval("ClosureGroup")338self.father = < int *> sage_malloc(n * sizeof(int))339if self.father is NULL:340raise MemoryError('allocating LabelledBranching')341self.act_perm = < int *> sage_malloc(n * sizeof(int))342if self.act_perm is NULL:343sage_free(self.father)344raise MemoryError('allocating LabelledBranching')345cdef int i346for i from 0 <= i < self.n:347self.father[i] = -1348349def __dealloc__(self):350"""351Dealloc.352"""353sage_free(self.father)354sage_free(self.act_perm)355356cpdef add_gen(self, GapElement_Permutation gen):357r"""358Add a further generator to the group and359update the complete labeled branching.360361EXAMPLES::362363sage: from sage.groups.perm_gps.partn_ref2.refinement_generic import LabelledBranching364sage: L = LabelledBranching(3)365sage: L.add_gen(libgap.eval('(1,2,3)'))366"""367from sage.libs.gap.libgap import libgap368369self.group = self.ClosureGroup(self.group, gen)370libgap.StabChain(self.group)371cdef GapElement h = self.group372cdef int i, i_1, j, f373374if libgap.IsTrivial(h).sage():375return376for i in range(self.n):377self.father[i] = -1378379while 1:380i = libgap.SmallestMovedPoint(h).sage()381i_1 = i - 1382f = self.father[i_1]383for j in libgap.Orbit(h, i).sage():384self.father[j - 1] = i_1385self.father[i_1] = f386h = libgap.Stabilizer(h, i)387if libgap.IsTrivial(h).sage():388break389390cdef bint has_empty_intersection(self, PartitionStack * part):391r"""392Pruning by automorphisms.393394The partition stack ``part`` encodes a right coset `G\pi` where395`G <= S_n` is a standard Young subgroup. On the other hand396this complete labeled branching of the group `A` naturally defines397a transversal of left coset representatives `T` of `S_n/A`.398399This function returns true if and only if `T \cap G\pi` is empty, see400[Feu13]_.401"""402cdef int i, j, k403for i from 0 <= i < self.n:404self.act_perm[part.entries[i]] = i405406for i from 0 <= i < self.n:407j = self.father[i]408if j != -1:409j = self.act_perm[j]410k = self.act_perm[i]411412while k < j:413if part.levels[k] <= part.depth:414return 1 # True415k += 1416return 0 # False417418def small_generating_set(self):419r"""420Return a small set of generators of the group stored by ``self``.421422EXAMPLES::423424sage: from sage.groups.perm_gps.partn_ref2.refinement_generic import LabelledBranching425sage: L = LabelledBranching(3)426sage: L.small_generating_set()427[]428sage: L.add_gen(libgap.eval('(1,2,3)'))429sage: L.small_generating_set()430[(1,2,3)]431"""432from sage.libs.gap.libgap import libgap433434return libgap.SmallGeneratingSet(self.group).sage()435436def get_order(self):437r"""438Return the order of the group stored by ``self``.439440EXAMPLES::441442sage: from sage.groups.perm_gps.partn_ref2.refinement_generic import LabelledBranching443sage: L = LabelledBranching(3)444sage: L.get_order()4451446sage: L.add_gen(libgap.eval('(1,2,3)'))447sage: L.get_order()4483449"""450from sage.libs.gap.libgap import libgap451452return libgap.Order(self.group).sage()453454cdef class PartitionRefinement_generic:455r"""456Implements the partition and refinement framework for457group actions `G \rtimes S_n` on `X^n` as described in458:mod:`sage.groups.perm_gps.partn_ref2.refinement_generic`.459"""460461def __cinit__(self, n, *args, **kwds):462"""463Init.464465EXAMPLES::466467sage: from sage.groups.perm_gps.partn_ref2.refinement_generic import PartitionRefinement_generic468sage: P = PartitionRefinement_generic(5)469"""470self._n = n # int471self._is_candidate_initialized = False472473self._known_automorphisms = LabelledBranching(n)474self._refine_vals_scratch = <long *> sage_malloc(n * sizeof(long))475if self._refine_vals_scratch is NULL:476raise MemoryError('allocating PartitionRefinement_generic')477478self._latex_debug_string = ""479self._fixed_not_minimized = []480self._fixed_minimized = []481self._allowance_best = []482self._nr_of_inner_min_unmin_calls=0483484def __dealloc__(self):485"""486Dealloc.487"""488PS_dealloc(self._part)489sage_free(self._refine_vals_scratch)490491#####################################################################492# The following functions have to be implemented by derived classes493#####################################################################494cdef bint _inner_min_(self, int pos, bint * inner_group_changed):495"""496Minimize the node by the action of the inner group on the i-th position.497498INPUT:499500- `pos` - A position in `range(self.n)`501- `inner_group_changed` - will be set to true if `G_y` got smaller502503OUTPUT:504505- `True` if and only if the actual node compares less or equal to506the candidate for the canonical form.507"""508raise NotImplementedError509510cdef bint _refine(self, bint *part_changed, bint inner_group_changed, bint first_step):511r"""512Refine the partition ``self._part``.513514Set ``part_changed`` to ``True`` if and only if the refinement let515to a smaller subgroup of `S_n`. This function also has to take516care on ``self._is_candidate_initialized``.517518OUTPUT:519520- `False` only if the actual node compares larger than the candidate521for the canonical form.522"""523raise NotImplementedError524525cdef tuple _store_state_(self):526r"""527Store the current state of the node to a tuple, such that it can be528restored by :meth:`_restore_state_`.529"""530raise NotImplementedError531532cdef void _restore_state_(self, tuple act_state):533r"""534The inverse of :meth:`_store_state_`.535"""536raise NotImplementedError537538cdef void _store_best_(self):539r"""540Store this leaf node as the actual best candidate for the canonical form.541"""542raise NotImplementedError543544cdef bint _minimization_allowed_on_col(self, int pos):545r"""546Decide if we are allowed to perform the inner minimization on position547``pos`` which is supposed to be a singleton.548"""549raise NotImplementedError550551def get_canonical_form(self):552r"""553Return the canonical form we have computed.554555EXAMPLES::556557sage: from sage.groups.perm_gps.partn_ref2.refinement_generic import PartitionRefinement_generic558sage: P = PartitionRefinement_generic(5)559sage: P.get_canonical_form()560Traceback (most recent call last):561...562NotImplementedError563"""564raise NotImplementedError565566def get_transporter(self):567r"""568Return the transporter element we have computed.569570EXAMPLES::571572sage: from sage.groups.perm_gps.partn_ref2.refinement_generic import PartitionRefinement_generic573sage: P = PartitionRefinement_generic(5)574sage: P.get_transporter()575Traceback (most recent call last):576...577NotImplementedError578"""579raise NotImplementedError580581def get_autom_gens(self):582r"""583Return a list of generators we have computed.584585EXAMPLES::586587sage: from sage.groups.perm_gps.partn_ref2.refinement_generic import PartitionRefinement_generic588sage: P = PartitionRefinement_generic(5)589sage: P.get_autom_gens()590Traceback (most recent call last):591...592NotImplementedError593"""594raise NotImplementedError595596def get_autom_order_permutation(self):597r"""598Return the order of the automorphism group we have computes599600EXAMPLES::601602sage: from sage.groups.perm_gps.partn_ref2.refinement_generic import PartitionRefinement_generic603sage: P = PartitionRefinement_generic(5)604sage: P.get_autom_order_permutation()6051606"""607return self._known_automorphisms.get_order()608609###############################################610# THE BACKTRACK ALGORITHM:611###############################################612cdef void _init_partition_stack(self, list partition):613r"""614Initialize the partition stack.615616If the list ``partition`` is not ``None``, it must617represent a coloring of the coordinates `\{0, \ldots, n-1\}`, i.e.618a list of disjoint lists.619"""620cdef bint inner_group_changed = False621if partition is None:622self._part = PS_new(self._n, 1)623else:624self._part = PS_from_list(partition)625# minimize singletons which already exist in this partition626self._fixed_not_minimized = PS_singletons(self._part)627self._inner_min_unminimized(&inner_group_changed)628if self._part is NULL:629sage_free(self._refine_vals_scratch)630raise MemoryError('initializing the partition stack')631632cdef void _start_Sn_backtrack(self):633r"""634Start the partition refinement algorithm.635"""636self._init_latex()637self._backtrack(True)638self._finish_latex()639640cdef void _backtrack(self, bint first_step = False):641r"""642Backtracking with pruning.643644- We first check if the subtree below this node contains any645valuable information otherwise we return. This test is based on646the subgroup of already known automorphisms.647- Perform a refinement. If this node is a leaf, we do all necessary updates.648- Else, we split the points of a fixed cell and backtrack recursively.649This leads to a depth-first-search construction of the search tree.650"""651self._latex_new_lvl()652cdef bint n_partition_changed = False, inner_group_changed=False653654if self._cut_by_known_automs():655self._latex_act_node("autcut")656self._latex_finish_lvl()657return658659if not self._inner_min_unminimized(&inner_group_changed):660self._latex_act_node("inner-min")661self._latex_finish_lvl()662return663664if not self._refine(&n_partition_changed, inner_group_changed, first_step):665self._latex_finish_lvl()666return667668if PS_is_discrete(self._part):669self._leaf_computations()670self._latex_finish_lvl()671return672673# store some important states of the current node674cdef int old_partition_depth = self._part.depth675cdef int old_fixed_minimized_len = len(self._fixed_minimized)676cdef int old_nr_of_inner_min_unmin_calls = self._nr_of_inner_min_unmin_calls677cdef list old_fixed_not_minimized = copy(self._fixed_not_minimized)678cdef tuple act_state = self._store_state_()679680# determine a cell C of the partition stack, which we will individualize681cdef bitset_t b682bitset_init(b, self._n)683PS_move_all_mins_to_front(self._part)684cdef int second_pos685cdef int smallest = PS_first_smallest(self._part, b, &second_pos,686self)687if second_pos != -1:688self._fixed_not_minimized.append(second_pos)689cdef int pos = smallest690self._latex_act_node()691692# recursively individualize the elements of the cell693while 1: # run over all elements in this cell694self._part.depth = old_partition_depth + 1695PS_clear(self._part)696PS_split_point(self._part, pos) # <- individualization!!!697self._fixed_not_minimized.append(pos)698self._backtrack() # backtracking699700#restore the old state and continue backtracking701self._part.depth = old_partition_depth702self._fixed_minimized = self._fixed_minimized[:old_fixed_minimized_len]703self._fixed_not_minimized = copy(old_fixed_not_minimized)704self._nr_of_inner_min_unmin_calls = old_nr_of_inner_min_unmin_calls705self._restore_state_(act_state) # we fixed at least one position, undo this change706707if second_pos != -1:708# special case when block has size 2709# update for second run on while, i.e. pos == second_pos710self._fixed_not_minimized.append(smallest)711712pos = bitset_next(b, pos + 1) # find the next candidate to individualize713if pos == -1:714# all candidates have been individualized!715break716717bitset_free(b)718self._latex_finish_lvl()719720cdef bint _inner_min_unminimized(self, bint *inner_group_changed):721r"""722Compute a subsequence `J = (j_0, \ldots, j_{l-1})`723of fixed coordinates, which were not yet used in724the inner minimization process (we call this sequence of used coordinates725`I`) and compute the `(I+J)`-semicanonical representative.726"""727cdef bint loc_inner_group_changed, reset_allowance_best = False728cdef int i, j, best_end, best_ind729730if self._is_candidate_initialized:731allowance_best = self._allowance_best[self._nr_of_inner_min_unmin_calls]732best_end = len(allowance_best)733best_ind = 0734else:735allowance_best = []736reset_allowance_best = True737inner_group_changed[0] = False738i = len(self._fixed_not_minimized)-1739while i>=0:740pos = self._fixed_not_minimized[i]741if self._minimization_allowed_on_col(pos):742for j from 0 <= j < self._n:743if self._part.entries[j] == pos:744my_final_pos = j745break746747if self._is_candidate_initialized:748if best_ind == best_end:749self._is_candidate_initialized = False750reset_allowance_best = True751else:752if allowance_best[best_ind] < my_final_pos:753return False754if allowance_best[best_ind] > my_final_pos:755self._is_candidate_initialized = False756allowance_best = allowance_best[:best_ind]757reset_allowance_best = True758best_ind += 1 # to avoid to come to this point again759760if not self._is_candidate_initialized:761allowance_best.append(my_final_pos)762763if not self._inner_min_(pos, &loc_inner_group_changed):764return False765766if not reset_allowance_best and not self._is_candidate_initialized:767allowance_best = allowance_best[:best_ind]768reset_allowance_best = True769770self._fixed_not_minimized.remove(pos)771self._fixed_minimized.append(pos)772inner_group_changed[0] |= loc_inner_group_changed773if loc_inner_group_changed:774i = len(self._fixed_not_minimized)775i -= 1776777if self._is_candidate_initialized:778if best_ind < best_end:779return False780else:781self._allowance_best = self._allowance_best[:self._nr_of_inner_min_unmin_calls]782self._allowance_best.append(allowance_best)783self._nr_of_inner_min_unmin_calls += 1784return True785786cdef bint _one_refinement(self, long * best, int begin, int end, bint *inner_group_changed,787bint *changed_partition, str refine_name):788r"""789Let ``self._refine_vals_scratch`` contain the result of the `(S_n)_C`-homomorphism790`\omega` and ``best`` be the result of `\omega` applied to the candidate.791792This method computes performs the refinements, i.e. updates the partition stack793and furthermore compares the result with ``best`` and decides794- if we could prune the subtree -> return value is set to False795- if the inner group has changed -> sets ``inner_group_changed`` to True796- if the partition has changed -> sets ``changed_partition`` to True797798The string ``refine_name`` is only neccessary for printing within the799latex output (if activated).800"""801cdef tuple res = PS_refinement(self._part, self._refine_vals_scratch, best, begin, end,802&self._is_candidate_initialized, changed_partition)803inner_group_changed[0] = False804if res[0]:805if changed_partition and self._cut_by_known_automs():806self._latex_act_node("autcut in " + refine_name)807return False808809if len( res[1] ) > 0:810self._fixed_not_minimized += res[1]811if self._inner_min_unminimized(inner_group_changed):812return True813else:814self._latex_act_node("inner-min in " + refine_name)815return False816return True817self._latex_act_node(refine_name)818return False819820cdef int len(self):821r"""822Return the degree of the acting symmetric group.823"""824return self._n825826cdef void _leaf_computations(self):827r"""828All neccessary computations which have to be performed in a leaf.829830There are to possibilities depending on the flag831``self._is_candidate_initialized``:832- ``True``: There is another leaf equal to this node. This defines an automorphism833which we add to the group of known automorphisms stored in834``self._known_automorphisms``.835- ``False``: We have shown, that the current leaf is smaller than all836other leaf nodes already visited. We set it to be the837candidate for the canonical form.838"""839from sage.libs.gap.libgap import libgap840cdef int i841842if self._is_candidate_initialized:843transp = libgap.PermList([self._part.entries[i] + 1 for i in range(self._n)])844# transp is the inverse of the actual transporter element (seen as action from left!)845self._known_automorphisms.add_gen(self._to_best_inverse * transp) # first apply _to_best_inverse then transp846self._latex_act_node("AUTOM")847else:848self._is_candidate_initialized = True849self._inner_min_order_best = self._fixed_minimized850self._to_best = libgap.PermList([self._part.entries[i] + 1 for i in range(self._n)])851self._to_best_inverse = self._to_best.Inverse()852853# call the method from the derived class854self._store_best_()855self._latex_act_node("NEW")856857cdef bint _cut_by_known_automs(self):858r"""859Return ``True`` if the subtree below this node does not contain860any *interesting* information.861"""862return self._is_candidate_initialized and self._known_automorphisms.has_empty_intersection(self._part)863864865###########################################################################866# These functions are used to produce some latex output:867# it writes the actual node868# to a 'tikz node' in latex. You just have to provide '_latex_act_node'869# in derived classes and to set870# BACKTRACK_WITHLATEX_DEBUG = 1 in the setup.py script when871# building this module!872###########################################################################873cdef void _latex_act_node(self, str comment="", int printlvl=0):874r"""875Append the actual node as a string of latex-commands to876``self._latex_debug_string``877"""878raise NotImplementedError # must be implemented by derived classes879880def _latex_view(self, title=None):881r"""882Evaluate the latex commands written to ``self._latex_debug_string``883and view the result.884885EXAMPLES::886887sage: from sage.groups.perm_gps.partn_ref2.refinement_generic import PartitionRefinement_generic888sage: P = PartitionRefinement_generic(5)889sage: P._latex_view()890sorry, no debug output was written. Set BACKTRACK_WITHLATEX_DEBUG to True if interested in this information891"""892if BACKTRACK_WITHLATEX_DEBUG:893from sage.misc.latex import latex, view894latex.extra_preamble('')895latex.add_to_preamble("\\usepackage{tikz}")896latex.add_to_preamble("\\usepackage{tikz-qtree}")897view(self._latex_debug_string, engine="pdflatex", title=title)898else:899print "sorry, no debug output was written. " + \900"Set BACKTRACK_WITHLATEX_DEBUG to True if interested in this information"901902cdef void _init_latex(self):903r"""904Add some initial commands to the string905``self._latex_debug_string``.906"""907if BACKTRACK_WITHLATEX_DEBUG:908from sage.misc.latex import LatexExpr909self._latex_debug_string = LatexExpr(910"\\resizebox{\\textwidth}{!}{\n" +911"\\begin{tikzpicture}\n" +912"\\tikzset{level distance=3cm, edge from parent/.style=" +913"{draw, edge from parent path={(\\tikzparentnode.south) -- (\\tikzchildnode.north)}}}\n" +914"\Tree")915self._latex_debug_string += "[."916self._latex_act_node()917918cdef void _finish_latex(self):919r"""920Add some final commands to the string921``self._latex_debug_string``.922"""923if BACKTRACK_WITHLATEX_DEBUG:924self._latex_debug_string += "]\n"925self._latex_debug_string += "\\end{tikzpicture} }\n"926927cdef void _latex_new_lvl(self):928r"""929Add some commands to the string ``self._latex_debug_string``,930in the case that the depth was increased during the backtracking.931"""932if BACKTRACK_WITHLATEX_DEBUG:933self._latex_debug_string += "[."934935cdef void _latex_finish_lvl(self):936r"""937Add some commands to the string ``self._latex_debug_string``,938in the case that we return to a node in the backtracking.939"""940if BACKTRACK_WITHLATEX_DEBUG:941self._latex_debug_string += "]\n"942943