Construct a free group returning (FreeGroup,(f_0,f_1,...,f_(n-1)).
Parameters:
symbols (str, Symbol/Expr or sequence of str, Symbol/Expr (may be empty)) –
Examples
>>> from.free_groupsimportfree_group>>> F,x,y,z=free_group("x, y, z")>>> F<free group on the generators (x, y, z)>>>> x**2*y**-1x**2*y**-1>>> type(_)<class 'sympy.combinatorics.free_groups.FreeGroupElement'>
Construct a free group and inject f_0,f_1,...,f_(n-1) as symbols
into the global namespace.
Parameters:
symbols (str, Symbol/Expr or sequence of str, Symbol/Expr (may be empty)) –
Examples
>>> from.free_groupsimportvfree_group>>> vfree_group("x, y, z")<free group on the generators (x, y, z)>>>> x**2*y**-2*zx**2*y**-2*z>>> type(_)<class 'sympy.combinatorics.free_groups.FreeGroupElement'>
Construct a free group returning (FreeGroup,(f_0,f_1,...,f_(n-1))).
Parameters:
symbols (str, Symbol/Expr or sequence of str, Symbol/Expr (may be empty)) –
Examples
>>> from.free_groupsimportxfree_group>>> F,(x,y,z)=xfree_group("x, y, z")>>> F<free group on the generators (x, y, z)>>>> y**2*x**-2*z**-1y**2*x**-2*z**-1>>> type(_)<class 'sympy.combinatorics.free_groups.FreeGroupElement'>
The result is given as a subgroup of Sn, except for the special cases n=1
(the group S2) and n=2 (the Klein 4-group) where that’s not possible
and embeddings in S2 and S4 respectively are given.
Permutations returned are for rotation of each of the slice
from the face up to the last face for each of the 3 sides (in this order):
front, right and bottom. Hence, the first n - 1 permutations are for the
slices from the front.
A Gray code is essentially a Hamiltonian walk on
a n-dimensional cube with edge length of one.
The vertices of the cube are represented by vectors
whose values are binary. The Hamilton walk visits
each vertex exactly once. The Gray code for a 3d
cube is [‘000’,’100’,’110’,’010’,’011’,’111’,’101’,
‘001’].
A Gray code solves the problem of sequentially
generating all possible subsets of n objects in such
a way that each subset is obtained from the previous
one by either deleting or adding a single object.
In the above example, 1 indicates that the object is
present, and 0 indicates that its absent.
Gray codes have applications in statistics as well when
we want to compute various statistics related to subsets
in an efficient manner.
References:
[1] Nijenhuis,A. and Wilf,H.S.(1978).
Combinatorial Algorithms. Academic Press.
[2] Knuth, D. (2011). The Art of Computer Programming, Vol 4
Addison Wesley
A ranking algorithm determines the position (or rank)
of a combinatorial object among all the objects w.r.t.
a given order. For example, the 4 bit binary reflected
Gray code (BRGC) ‘0101’ has a rank of 6 as it appears in
the 6th position in the canonical ordering of the family
of 4 bit Gray codes.
Returns the direct product of several groups as a permutation group.
This is implemented much like the __mul__ procedure for taking the direct
product of two permutation groups, but the idea of shifting the
generators is realized in the case of an arbitrary number of groups.
A call to DirectProduct(G1, G2, …, Gn) is generally expected to be faster
than a call to G1*G2*…*Gn (and thus the need for this algorithm).
Returns the direct product of cyclic groups with the given orders.
According to the structure theorem for finite abelian groups ([1]),
every finite abelian group can be written as the direct product of
finitely many cyclic groups.
Generates the alternating group on n elements as a permutation group.
For n>2, the generators taken are (012),(012...n-1) for
n odd
and (012),(12...n-1) for n even (See [1], p.31, ex.6.9.).
After the group is generated, some of its basic properties are set.
The cases n=1,2 are handled separately.
Generates the dihedral group D_n as a permutation group.
The dihedral group D_n is the group of symmetries of the regular
n-gon. The generators taken are the n-cycle a=(012...n-1)
(a rotation of the n-gon) and b=(0n-1)(1n-2)...
(a reflection of the n-gon) in cycle rotation. It is easy to see that
these satisfy a**n=b**2=1 and bab=~a so they indeed generate
D_n (See [1]). After the group is generated, some of its basic properties
are set.
Generates the symmetric group on n elements as a permutation group.
The generators taken are the n-cycle
(012...n-1) and the transposition (01) (in cycle notation).
(See [1]). After the group is generated, some of its basic properties
are set.
In number theory and combinatorics, a partition of a positive integer,
n, also called an integer partition, is a way of writing n as a
list of positive integers that sum to n. Two partitions that differ only
in the order of summands are considered to be the same partition; if order
matters then the partitions are referred to as compositions. For example,
4 has five partitions: [4], [3, 1], [2, 2], [2, 1, 1], and [1, 1, 1, 1];
the compositions [1, 2, 1] and [1, 1, 2] are the same as partition
[2, 1, 1].
Returns the “restricted growth string” of the partition.
The RGS is returned as a list of indices, L, where L[i] indicates
the block in which element i appears. For example, in a partition
of 3 elements (a, b, c) into 2 blocks ([c], [a, b]) the RGS is
[1, 1, 0]: “a” is in block 1, “b” is in block 1 and “c” is in block 0.
Creates a set partition from a restricted growth string.
The indices given in rgs are assumed to be the index
of the element as given in elements as provided (the
elements are not sorted by this routine). Block numbering
starts from 0. If any block was not referenced in rgs
an error will be raised.
PermutationGroup([p1, p2, …, pn]) returns the permutation group
generated by the list of permutations. This group can be supplied
to Polyhedron if one desires to decorate the elements to which the
indices of the permutation refer.
These are passed as permutations to PermutationGroup:
>>> G=PermutationGroup(F,R,D)>>> G.order()3674160
The group can be supplied to a Polyhedron in order to track the
objects being moved. An example involving the 2x2 Rubik’s cube is
given there, but here is a simple demonstration:
For a permutation group G, a base is a sequence of points
B = (b_1, b_2, …, b_k) such that no element of G apart
from the identity fixes all the points in B. The concepts of
a base and strong generating set and their applications are
discussed in depth in [1], pp. 87-89 and [2], pp. 55-57.
An alternative way to think of B is that it gives the
indices of the stabilizer cosets that contain more than the
identity permutation.
Swap two consecutive base points in base and strong generating set.
If a base for a group G is given by (b_1, b_2, …, b_k), this
function returns a base (b_1, b_2, …, b_{i+1}, b_i, …, b_k),
where i is given by pos, and a strong generating set relative
to that base. The original base and strong generating set are not
modified.
The randomized version (default) is of Las Vegas type.
Parameters:
base – The base and strong generating set.
strong_gens – The base and strong generating set.
pos – The position at which swapping is performed.
randomized – A switch between randomized and deterministic version.
transversals – The transversals for the basic orbits, if known.
basic_orbits – The basic orbits, if known.
strong_gens_distr – The strong generators distributed by basic stabilizers, if known.
Returns:
base is the new base, and strong_gens is a generating set
relative to it.
The deterministic version of the algorithm is discussed in
[1], pp. 102-103; the randomized version is discussed in [1], p.103, and
[2], p.98. It is of Las Vegas type.
Notice that [1] contains a mistake in the pseudocode and
discussion of BASESWAP: on line 3 of the pseudocode,
|beta_{i+1}^{leftlangle Trightrangle}| should be replaced by
|beta_{i}^{leftlangle Trightrangle}|, and the same for the
discussion of the algorithm.
Return the basic orbits relative to a base and strong generating set.
If (b_1, b_2, …, b_k) is a base for a group G, and
G^{(i)} = G_{b_1, b_2, …, b_{i-1}} is the i-th basic stabilizer
(so that G^{(1)} = G), the i-th basic orbit relative to this base
is the orbit of b_i under G^{(i)}. See [1], pp. 87-89 for more
information.
Return basic transversals relative to a base and strong generating set.
The basic transversals are transversals of the basic orbits. They
are provided as a list of dictionaries, each dictionary having
keys - the elements of one of the basic orbits, and values - the
corresponding transversal elements. See [1], pp. 87-89 for more
information.
The center for a group G is defined as
Z(G) = {zin G | forall gin G, zg = gz },
the set of elements of G that commute with all elements of G.
It is equal to the centralizer of G inside G, and is naturally a
subgroup of G ([9]).
The centralizer of a set of permutations S inside
a group G is the set of elements of G that commute with all
elements of S:
`C_G(S) = \{ g \in G | gs = sg \forall s \in S\}` ([10])
Usually, S is a subset of G, but if G is a proper subgroup of
the full symmetric group, we allow for S to have elements outside
G.
It is naturally a subgroup of G; the centralizer of a permutation
group is equal to the centralizer of any set of generators for that
group, since any element commuting with the generators commutes with
any product of the generators.
Parameters:
other – a permutation group/list of permutations/single permutation
For a permutation group K and subgroups G, H, the
commutator of G and H is defined as the group generated
by all the commutators [g, h] = hgh^{-1}g^{-1} for g in G and
h in H. It is naturally a subgroup of K ([1], p.27).
The commutator of two subgroups H, G is equal to the normal closure
of the commutators of all the generators, i.e. hgh^{-1}g^{-1} for h
a generator of H and g a generator of G ([1], p.28)
If g is an element of G it can be written as a product
of factors drawn from the cosets of G’s stabilizers. To see
if g is one of the actual generators defining the group use
G.has(g).
If strict is not True, g will be resized, if necessary,
to match the size of permutations in self.
If g is an element of G then it can be written as the product
of permutations drawn from the Schreier-Sims coset decomposition,
The permutations returned in f are those for which
the product gives g: g=f[n]*...f[1]*f[0] where n=len(B)
and B=G.base. f[i] is one of the permutations in
self._basic_orbits[i].
If factor_index==True,
returns a tuple [b[0],..,b[n]], where b[i]
belongs to self._basic_orbits[i]
Thus, it can be written as a product of factors (up to
3) drawn from u. See below that a factor from u1 and u2
and the Identity permutation have been used:
Returns the size of the permutations in the group.
The number of permutations comprising the group is given by
len(group); the number of permutations that can be generated
by the group is given by group.order().
The derived series for a group G is defined as
G = G_0 > G_1 > G_2 > ldots where G_i = [G_{i-1}, G_{i-1}],
i.e. G_i is the derived subgroup of G_{i-1}, for
iinmathbb{N}. When we have G_k = G_{k-1} for some
kinmathbb{N}, the series terminates.
Returns:
A list of permutation groups containing the members of the derived
The derived subgroup, or commutator subgroup is the subgroup generated
by all commutators [g, h] = hgh^{-1}g^{-1} for g, hin G ; it is
equal to the normal closure of the set of commutators of the generators
([1], p.28, [11]).
Monte Carlo test for the symmetric/alternating group for degrees
>= 8.
More specifically, it is one-sided Monte Carlo with the
answer True (i.e., G is symmetric/alternating) guaranteed to be
correct, and the answer False being incorrect with probability eps.
Notes
The algorithm itself uses some nontrivial results from group theory and
number theory:
1) If a transitive group G of degree n contains an element
with a cycle of length n/2<p<n-2 for p a prime, G is the
symmetric or alternating group ([1], pp. 81-82)
2) The proportion of elements in the symmetric/alternating group having
the property described in 1) is approximately log(2)/log(n)
([1], p.82; [2], pp. 226-227).
The helper function _check_cycles_alt_sym is used to
go over the cycles in a permutation and look for ones satisfying 1).
A group G is nilpotent if it has a central series of finite length.
Alternatively, G is nilpotent if its lower central series terminates
with the trivial group. Every nilpotent group is also solvable
([1], p.29, [12]).
G is normal in gr if
for each g2 in G, g1 in gr, g=g1*g2*g1**-1 belongs to G
It is sufficient to check this for each g1 in gr.generators and
g2 in G.generators.
A permutation group G acting on a set S is called primitive if
S contains no nontrivial block under the action of G
(a block is nontrivial if its cardinality is more than 1).
Notes
The algorithm is described in [1], p.83, and uses the function
minimal_block to search for blocks of the form {0, k} for k
ranging over representatives for the orbits of G_0, the stabilizer of
0. This algorithm has complexity O(n^2) where n is the degree
of the group, and will perform badly if G_0 is small.
There are two implementations offered: one finds G_0
deterministically using the function stabilizer, and the other
(default) produces random elements of G_0 using random_stab,
hoping that they generate a subgroup of G_0 with not too many more
orbits than G_0 (this is suggested in [1], p.83). Behavior is changed
by the randomized flag.
The lower central series for a group G is the series
G = G_0 > G_1 > G_2 > ldots where
G_k = [G, G_{k-1}], i.e. every term after the first is equal to the
commutator of G and the previous term in G1 ([1], p.29).
Return type:
A list of permutation groups in the order G = G_0, G_1, G_2, ldots
Multiply n randomly selected permutations from
pgroup together, starting with the identity
permutation. If n is a list of integers, those
integers will be used to select the permutations and they
will be applied in L to R order: make_perm((A, B, C)) will
give CBA(I) where I is the identity permutation.
seed is used to set the seed for the random selection
of permutations from pgroup. If this is a list of integers,
the corresponding permutations from pgroup will be selected
in the order give. This is mainly used for testing purposes.
Maximum proper divisor of the degree of a permutation group.
Notes
Obviously, this is the degree divided by its minimal proper divisor
(larger than 1, if one exists). As it is guaranteed to be prime,
the sieve from sympy.ntheory is used.
This function is also used as an optimization tool for the functions
minimal_block and _union_find_merge.
For a transitive group, finds the block system generated by
points.
If a group G acts on a set S, a nonempty subset B of S
is called a block under the action of G if for all g in G
we have gB=B (g fixes B) or gB and B have no
common points (g moves B entirely). ([1], p.23; [6]).
The distinct translates gB of a block B for g in G
partition the set S and this set of translates is known as a block
system. Moreover, we obviously have that all blocks in the partition
have the same size, hence the block size divides |S| ([1], p.23).
A G-congruence is an equivalence relation ~ on the set S
such that a~b implies g(a)~g(b) for all g in G.
For a transitive group, the equivalence classes of a G-congruence
and the blocks of a block system are the same thing ([1], p.23).
The algorithm below checks the group for transitivity, and then finds
the G-congruence generated by the pairs (p_0,p_1),(p_0,p_2),...,(p_0,p_{k-1}) which is the same as finding the maximal block
system (i.e., the one with minimum block size) such that
p_0,...,p_{k-1} are in the same block ([1], p.83).
It is an implementation of Atkinson’s algorithm, as suggested in [1],
and manipulates an equivalence relation on the set S using a
union-find data structure. The running time is just above
O(|points||S|). ([1], pp. 83-87; [7]).
Return the normal closure of a subgroup/set of permutations.
If S is a subset of a group G, the normal closure of A in G
is defined as the intersection of all normal subgroups of G that
contain A ([1], p.14). Alternatively, it is the group generated by
the conjugates x^{-1}yx for x a generator of G and y a
generator of the subgroup \left\langleS\right\rangle generated by
S (for some chosen generating set for \left\langleS\right\rangle)
([1], p.73).
Parameters:
other – a subgroup/list of permutations/single permutation
k – an implementation-specific parameter that determines the number
of conjugates that are adjoined to other at once
The algorithm is described in [1], pp. 73-74; it makes use of the
generation of random elements for permutation groups by the product
replacement algorithm.
Compute the orbit of alpha {g(alpha) | g in G} as a set.
The time complexity of the algorithm used here is O(|Orb|*r) where
|Orb| is the size of the orbit and r is the number of generators of
the group. For a more detailed analysis, see [1], p.78, [2], pp. 19-21.
Here alpha can be a single point, or a list of points.
If alpha is a single point, the ordinary orbit is computed.
if alpha is a list of points, there are three available options:
‘union’ - computes the union of the orbits of the points in the list
‘tuples’ - computes the orbit of the list interpreted as an ordered
tuple under the group action ( i.e., g((1,2,3)) = (g(1), g(2), g(3)) )
‘sets’ - computes the orbit of the list interpreted as a sets
If beta is not in the orbit of alpha, the function returns
False. This implementation makes use of the schreier vector.
For a proof of correctness, see [1], p.80
Computes a transversal for the orbit of alpha as a set.
For a permutation group G, a transversal for the orbit
Orb = {g(alpha) | g in G} is a set
{g_beta | g_beta(alpha) = beta} for beta in Orb.
Note that there may be more than one possible transversal.
If pairs is set to True, it returns the list of pairs
(beta, g_beta). For a proof of correctness, see [1], p.79
Return the pointwise stabilizer for a set of points.
For a permutation group G and a set of points
{p_1, p_2,ldots, p_k}, the pointwise stabilizer of
p_1, p_2, ldots, p_k is defined as
G_{p_1,ldots, p_k} =
{gin G | g(p_i) = p_i forall iin{1, 2,ldots,k}} ([1],p20).
It is a subgroup of G.
When incremental == True,
rather than the obvious implementation using successive calls to
.stabilizer(), this uses the incremental Schreier-Sims algorithm
to obtain a base with starting segment - the given points.
Return a random group element using product replacement.
For the details of the product replacement algorithm, see
_random_pr_init In random_pr the actual ‘product replacement’
is performed. Notice that if the attribute _random_gens
is empty, it needs to be initialized by _random_pr_init.
It computes the generators of the chain of stabilizers
G > G_{b_1} > .. > G_{b1,..,b_r} > 1
in which G_{b_1,..,b_i} stabilizes b_1,..,b_i,
and the corresponding s cosets.
An element of the group can be written as the product
h_1*..*h_s.
Extend a sequence of points and generating set to a base and strong
generating set.
Parameters:
base – The sequence of points to be extended to a base. Optional
parameter with default value [].
gens – The generating set to be extended to a strong generating set
relative to the base obtained. Optional parameter with default
value self.generators.
Returns:
base is the base obtained, and strong_gens is the strong
generating set relative to it. The original parameters base,
gens remain unchanged.
This version of the Schreier-Sims algorithm runs in polynomial time.
There are certain assumptions in the implementation - if the trivial
group is provided, base and gens are returned immediately,
as any sequence of points is a base for the trivial group. If the
identity is present in the generators gens, it is removed as
it is a redundant generator.
The implementation is described in [1], pp. 90-93.
The randomized Schreier-Sims algorithm takes the sequence base
and the generating set gens, and extends base to a base, and
gens to a strong generating set relative to that base with
probability of a wrong answer at most 2^{-consec_succ},
provided the random generators are sufficiently random.
Parameters:
base – The sequence to be extended to a base.
gens – The generating set to be extended to a strong generating set.
consec_succ – The parameter defining the probability of a wrong answer.
_random_prec – An internal parameter used for testing purposes.
Returns:
base is the base and strong_gens is the strong generating
set relative to it.
The algorithm is described in detail in [1], pp. 97-98. It extends
the orbits orbs and the permutation groups stabs to
basic orbits and basic stabilizers for the base and strong generating
set produced in the end.
The idea of the extension process
is to “sift” random group elements through the stabilizer chain
and amend the stabilizers/orbits along the way when a sift
is not successful.
The helper function _strip is used to attempt
to decompose a random group element according to the current
state of the stabilizer chain and report whether the element was
fully decomposed (successful sift) or not (unsuccessful sift). In
the latter case, the level at which the sift failed is reported and
used to amend stabs, base, gens and orbs accordingly.
The halting condition is for consec_succ consecutive successful
sifts to pass. This makes sure that the current base and gens
form a BSGS with probability at least 1 - 1/text{consec_succ}.
The Schreier vector efficiently stores information
about the orbit of alpha. It can later be used to quickly obtain
elements of the group that send alpha to a particular element
in the orbit. Notice that the Schreier vector depends on the order
in which the group generators are listed. For a definition, see [3].
Since list indices start from zero, we adopt the convention to use
“None” instead of 0 to signify that an element doesn’t belong
to the orbit.
For the algorithm and its correctness, see [2], pp.78-80.
Return a strong generating set from the Schreier-Sims algorithm.
A generating set S = {g_1, g_2, …, g_t} for a permutation group
G is a strong generating set relative to the sequence of points
(referred to as a “base”) (b_1, b_2, …, b_k) if, for
1 leq i leq k we have that the intersection of the pointwise
stabilizer G^{(i+1)} := G_{b_1, b_2, …, b_i} with S generates
the pointwise stabilizer G^{(i+1)}. The concepts of a base and
strong generating set and their applications are discussed in depth
in [1], pp. 87-89 and [2], pp. 55-57.
Find the subgroup of all elements satisfying the property prop.
This is done by a depth-first search with respect to base images that
uses several tests to prune the search tree.
Parameters:
prop – The property to be used. Has to be callable on group elements
and always return True or False. It is assumed that
all group elements satisfying prop indeed form a subgroup.
base – A base for the supergroup.
strong_gens – A strong generating set for the supergroup.
tests – A list of callables of length equal to the length of base.
These are used to rule out group elements by partial base images,
so that tests[l](g) returns False if the element g is known
not to satisfy prop base on where g sends the first l+1 base
points.
init_subgroup – if a subgroup of the sought group is
known in advance, it can be passed to the function as this
parameter.
Returns:
The subgroup of all elements satisfying prop. The generating
set for this group is guaranteed to be a strong generating set
relative to the base base.
This function is extremely lenghty and complicated and will require
some careful attention. The implementation is described in
[1], pp. 114-117, and the comments for the code here follow the lines
of the pseudocode in the book for clarity.
The complexity is exponential in general, since the search process by
itself visits all members of the supergroup. However, there are a lot
of tests which are used to prune the search tree, and users can define
their own tests via the tests parameter, so in practice, and for
some computations, it’s not terrible.
A crucial part in the procedure is the frequent base change performed
(this is line 11 in the pseudocode) in order to obtain a new basic
stabilizer. The book mentiones that this can be done by using
.baseswap(...), however the current imlementation uses a more
straightforward way to find the next basic stabilizer - calling the
function .stabilizer(...) on the previous basic stabilizer.
A permutation group G acting on Omega = {0, 1, …, n-1} is
k-fold transitive, if, for any k points
(a_1, a_2, …, a_k)inOmega and any k points
(b_1, b_2, …, b_k)inOmega there exists gin G such that
g(a_1)=b_1, g(a_2)=b_2, …, g(a_k)=b_k
The degree of transitivity of G is the maximum k such that
G is k-fold transitive. ([8])
Wrapper around dict which provides the functionality of a disjoint cycle.
A cycle shows the rule to use to move subsets of elements to obtain
a permutation. The Cycle class is more flexible than Permutation in
that 1) all elements need not be present in order to investigate how
multiple cycles act in sequence and 2) it can contain singletons:
>>> from.permutationsimportPerm,Cycle
A Cycle will automatically parse a cycle given as a tuple on the rhs:
>>> Cycle(1,2)(2,3)(1 3 2)
The identity cycle, Cycle(), can be used to start a product:
>>> Cycle()(1,2)(2,3)(1 3 2)
The array form of a Cycle can be obtained by calling the list
method (or passing it to the list function) and all elements from
0 will be shown:
If a larger (or smaller) range is desired use the list method and
provide the desired size – but the Cycle cannot be truncated to
a size smaller than the largest element that is out of place:
The underlying structure of the Cycle is a dictionary and although
the __iter__ method has been redefined to give the array form of the
cycle, the underlying dictionary items are still available with the
such methods as items():
Return the cycles as an explicit list starting from 0 up
to the greater of the largest value in the cycles and size.
Truncation of trailing unmoved items will occur when size
is less than the maximum element in the cycle; if this is
desired, setting size=-1 will guarantee such trimming.
A permutation, alternatively known as an ‘arrangement number’ or ‘ordering’
is an arrangement of the elements of an ordered list into a one-to-one
mapping with itself. The permutation of a given arrangement is given by
indicating the positions of the elements after re-arrangement [2]. For
example, if one started with elements [x, y, a, b] (in that order) and
they were reordered as [x, y, b, a] then the permutation would be
[0, 1, 3, 2]. Notice that (in SymPy) the first element is always referred
to as 0 and the permutation uses the indices of the elements in the
original ordering, not the elements (a, b, etc…) themselves.
In the 2-line form, the elements and their final positions are shown
as a matrix with 2 rows:
[0 1 2 … n-1]
[p(0) p(1) p(2) … p(n-1)]
Since the first line is always range(n), where n is the size of p,
it is sufficient to represent the permutation by the second line,
referred to as the “array form” of the permutation. This is entered
in brackets as the argument to the Permutation class:
In disjoint cycle notation, only the elements that have shifted are
indicated. In the above case, the 2 and 1 switched places. This can
be entered in two ways:
>>> Permutation(1,2)==Permutation([[1,2]])==pTrue
Only the relative ordering of elements in a cycle matter:
Caution: when the cycles have common elements
between them then the order in which the
permutations are applied matters. The
convention is that the permutations are
applied from right to left. In the following, the
transposition of elements 2 and 3 is followed
by the transposition of elements 1 and 2:
If the first and second elements had been
swapped first, followed by the swapping of the second
and third, the result would have been [0, 2, 3, 1].
If, for some reason, you want to apply the cycles
in the order they are entered, you can simply reverse
the order of cycles:
Caution: no singleton containing an element larger than the largest
in any previous cycle can be entered. This is an important difference
in how Permutation and Cycle handle the __call__ syntax. A singleton
argument at the start of a Permutation performs instantiation of the
Permutation and is permitted:
>>> Permutation(5)Permutation([], size=6)
A singleton entered after instantiation is a call to the permutation
– a function call – and if the argument is out of range it will
trigger an error. For this reason, it is better to start the cycle
with the singleton:
The following fails because there is is no element 3:
>>> Permutation(1,2)(3)Traceback (most recent call last):...IndexError: list index out of range
This is ok: only the call to an out of range singleton is prohibited;
otherwise the permutation autosizes:
The identity permutation is a permutation in which no element is out of
place. It can be entered in a variety of ways. All the following create
an identity permutation of size 4:
2) Regardless of the setting, a list of elements in the array for cyclic
form can be obtained and either of those can be copied and supplied as
the argument to Permutation:
The number of permutations on a set of n elements is given by n! and is
called the cardinality.
>>> p.size4>>> p.cardinality24
A given permutation has a rank among all the possible permutations of the
same elements, but what that rank is depends on how the permutations are
enumerated. (There are a number of different methods of doing so.) The
lexicographic rank is given by the rank method and this rank is used to
increment a permutation with addition/subtraction:
Computes the adjacency distance between two permutations.
This metric counts the number of times a pair i,j of jobs is
adjacent in both p and p’. If n_adj is this quantity then
the adjacency distance is n - n_adj - 1 [1]
[1] Reeves, Colin R. Landscapes, Operators and Heuristic search, Annals
of Operational Research, 86, pp 473-490. (1999)
Computes the precedence distance between two permutations.
Suppose p and p’ represent n jobs. The precedence metric
counts the number of times a job j is preceded by job i
in both p and p’. This metric is commutative.
The inversion vector consists of elements whose value
indicates the number of elements in the permutation
that are lesser than it and lie on its right hand side.
The inversion vector is the same as the Lehmer encoding of a
permutation.
Computes the number of inversions of a permutation.
An inversion is where i > j but p[i] < p[j].
For small length of p, it iterates over all i and j
values and calculates the number of inversions.
For large length of p, it uses a variation of merge
sort to calculate the number of inversions.
Return as a permutation the shuffling of range(n) using the Josephus
scheme in which every m-th item is selected until all have been chosen.
The returned permutation has elements listed by the order in which they
were selected.
The parameter s stops the selection process when there are s
items remaining and these are selected by continuing the selection,
counting by 1 rather than by m.
Consider selecting every 3rd item from 6 until only 2 remain:
Return the permutation as an explicit list, possibly
trimming unmoved elements if size is less than the maximum
element in the permutation; if this is desired, setting
size=-1 will guarantee such trimming.
Returns the next permutation in Trotter-Johnson order.
If self is the last permutation it returns None.
See [4] section 2.4. If it is desired to generate all such
permutations, they can be generated in order more quickly
with the generate_bell function.
The parity of a permutation reflects the parity of the
number of inversions in the permutation, i.e., the
number of pairs of x and y such that x>y but p[x]<p[y].
The PSG is one of the symmetry groups of the Platonic solids.
There are three polyhedral groups: the tetrahedral group
of order 12, the octahedral group of order 24, and the
icosahedral group of order 60.
All doctests have been given in the docstring of the
constructor of the object.
Apply a permutation to the polyhedron in place. The permutation
may be given as a Permutation instance or an integer indicating
which permutation from pgroup of the Polyhedron should be
applied.
This is an operation that is analogous to rotation about
an axis by a fixed increment.
Notes
When a Permutation is applied, no check is done to see if that
is a valid permutation for the Polyhedron. For example, a cube
could be given a permutation which effectively swaps only 2
vertices. A valid permutation (that rotates the object in a
physical way) will be obtained if one only uses
permutations from the pgroup of the Polyhedron. On the other
hand, allowing arbitrary rotations (applications of permutations)
gives a way to follow named elements rather than indices since
Polyhedron allows vertices to be named while Permutation works
only with indices.
The Prufer correspondence is an algorithm that describes the
bijection between labeled trees and the Prufer code. A Prufer
code of a labeled tree is unique up to isomorphism and has
a length of n - 2.
Prufer sequences were first used by Heinz Prufer to give a
proof of Cayley’s formula.
Return a list of edges and the number of nodes from the given runs
that connect nodes in an integer-labelled tree.
All node numbers will be shifted so that the minimum node is 0. It is
not a problem if edges are repeated in the runs; only unique edges are
returned. There is no assumption made about what the range of the node
labels should be, but all nodes from the smallest through the largest
must be present.
This sequence is found by removing the highest numbered vertex,
recording the node it was attached to, and continuing until only
two vertices remain. The Prufer sequence is the list of recorded nodes.
We generate subsets using essentially two techniques,
binary enumeration and lexicographic enumeration.
The Subset class takes two arguments, the first one
describes the initial subset to consider and the second
describes the superset.
canonicalization of a tensor with respect to free indices
choosing the minimum with respect to lexicographical ordering
in the free indices
base, gens BSGS for slot permutation group
g permutation representing the tensor
num_free number of free indices
The indices must be ordered with first the free indices
see explanation in double_coset_can_rep
The algorithm is a variation of the one given in [2].
Consider the product of Riemann tensors
T=R^{a}_{d0}^{d1,d2}*R_{d2,d1}^{d0,b}
The order of the indices is [a,b,d0,-d0,d1,-d1,d2,-d2]
The permutation corresponding to the tensor is
g=[0,3,4,6,7,5,2,1,8,9]
In particular a is position 0, b is in position 9.
Use the slot symmetries to get T is a form which is the minimal
in lexicographic order in the free indices a and b, e.g.
-R^{a}_{d0}^{d1,d2}*R^{b,d0}_{d2,d1} corresponding to
[0,3,4,6,1,2,7,5,9,8]
dummies (list representing the dummy indices) – it can be a list of dummy indices of the same type
or a list of lists of dummy indices, one list for each
type of index;
the dummy indices must come after the free indices,
and put in order contravariant, covariant
[d0, -d0, d1,-d1,…]
msym (symmetry of the metric(s)) – it can be an integer or a list;
in the first case it is the symmetry of the dummy index metric;
in the second case it is the list of the symmetries of the
index metric for each type
v (list, (base_i, gens_i, n_i, sym_i) for tensors of type i) –
base_i (BSGS for tensors of this type.) – The BSGS should have minimal base under lexicographic ordering;
if not, an attempt is made do get the minimal BSGS;
in case of failure,
canonicalize_naive is used, which is much slower.
gens_i (BSGS for tensors of this type.) – The BSGS should have minimal base under lexicographic ordering;
if not, an attempt is made do get the minimal BSGS;
in case of failure,
canonicalize_naive is used, which is much slower.
n_i (number of tensors of type i.) –
sym_i (symmetry under exchange of component tensors of type i.) –
Both for msym and sym_i the cases are
None no symmetry
0 commuting
1 anticommuting
Returns:
0 if the tensor is zero, else return the array form of
the permutation representing the canonical form of the tensor.
Algorithm
=========
First one uses canonical_free to get the minimum tensor under
lexicographic order, using only the slot symmetries.
If the component tensors have not minimal BSGS, it is attempted
to find it; if the attempt fails canonicalize_naive
is used instead.
Compute the residual slot symmetry keeping fixed the free indices
using tensor_gens(base, gens, list_free_indices, sym).
Reduce the problem eliminating the free indices.
Then use double_coset_can_rep and lift back the result reintroducing
Butler-Portugal algorithm for tensor canonicalization with dummy indices
dummies
list of lists of dummy indices,
one list for each type of index;
the dummy indices are put in order contravariant, covariant
[d0, -d0, d1, -d1, …].
sym
list of the symmetries of the index metric for each type.
possible symmetries of the metrics
0 symmetric
1 antisymmetric
None no symmetry
b_S
base of a minimal slot symmetry BSGS.
sgens
generators of the slot symmetry BSGS.
S_transversals
transversals for the slot BSGS.
g
permutation representing the tensor.
Return 0 if the tensor is zero, else return the array form of
the permutation representing the canonical form of the tensor.
A tensor with dummy indices can be represented in a number
of equivalent ways which typically grows exponentially with
the number of indices. To be able to establish if two tensors
with many indices are equal becomes computationally very slow
in absence of an efficient algorithm.
The Butler-Portugal algorithm [3] is an efficient algorithm to
put tensors in canonical form, solving the above problem.
Portugal observed that a tensor can be represented by a permutation,
and that the class of tensors equivalent to it under slot and dummy
symmetries is equivalent to the double coset D*g*S
(Note: in this documentation we use the conventions for multiplication
of permutations p, q with (p*q)(i) = p[q[i]] which is opposite
to the one used in the Permutation class)
Using the algorithm by Butler to find a representative of the
double coset one can find a canonical form for the tensor.
To see this correspondence,
let g be a permutation in array form; a tensor with indices ind
(the indices including both the contravariant and the covariant ones)
can be written as
t = T(ind[g[0],…, ind[g[n-1]]),
where n= len(ind);
g has size n + 2, the last two indices for the sign of the tensor
(trick introduced in [4]).
A slot symmetry transformation s is a permutation acting on the slots
t -> T(ind[(g*s)[0]],…, ind[(g*s)[n-1]])
A dummy symmetry transformation acts on ind
t -> T(ind[(d*g)[0]],…, ind[(d*g)[n-1]])
Being interested only in the transformations of the tensor under
these symmetries, one can represent the tensor by g, which transforms
as
g -> d*g*s, so it belongs to the coset D*g*S.
Let us explain the conventions by an example.
Given a tensor T^{d3 d2 d1}{}_{d1 d2 d3} with the slot symmetries
T^{a0 a1 a2 a3 a4 a5} = -T^{a2 a1 a0 a3 a4 a5}
T^{a0 a1 a2 a3 a4 a5} = -T^{a4 a1 a2 a3 a0 a5}
and symmetric metric, find the tensor equivalent to it which
is the lowest under the ordering of indices:
lexicographic ordering d1, d2, d3 then and contravariant index
before covariant index; that is the canonical form of the tensor.
The canonical form is -T^{d1 d2 d3}{}_{d1 d2 d3}
obtained using T^{a0 a1 a2 a3 a4 a5} = -T^{a2 a1 a0 a3 a4 a5}.
To convert this problem in the input for this function,
use the following labelling of the index names
(- for covariant for short) d1, -d1, d2, -d2, d3, -d3
T^{d3 d2 d1}{}_{d1 d2 d3} corresponds to g = [4, 2, 0, 1, 3, 5, 6, 7]
where the last two indices are for the sign
= 0 since two of these lines have tensors differ only for the sign.
The double coset D*g*S consists of permutations h = d*g*s corresponding
to equivalent tensors; if there are two h which are the same apart
from the sign, return zero; otherwise
choose as representative the tensor with indices
ordered lexicographically according to [d1, -d1, d2, -d2, d3, -d3]
that is rep = min(D*g*S) = min([d*g*s for d in D for s in S])
The indices are fixed one by one; first choose the lowest index
for slot 0, then the lowest remaining index for slot 1, etc.
Doing this one obtains a chain of stabilizers
S -> S_{b0} -> S_{b0,b1} -> … and
D -> D_{p0} -> D_{p0,p1} -> …
where [b0, b1, …] = range(b) is a base of the symmetric group;
the strong base b_S of S is an ordered sublist of it;
therefore it is sufficient to compute once the
strong base generators of S using the Schreier-Sims algorithm;
the stabilizers of the strong base generators are the
strong base generators of the stabilizer subgroup.
dbase = [p0, p1, …] is not in general in lexicographic order,
so that one must recompute the strong base generators each time;
however this is trivial, there is no need to use the Schreier-Sims
algorithm for D.
The algorithm keeps a TAB of elements (s_i, d_i, h_i)
where h_i = d_i*g*s_i satisfying h_i[j] = p_j for 0 <= j < i
starting from s_0 = id, d_0 = id, h_0 = g.
The equations h_0[0] = p_0, h_1[1] = p_1,… are solved in this order,
choosing each time the lowest possible value of p_i
For j < i
d_i*g*s_i*S_{b_0,…,b_{i-1}}*b_j = D_{p_0,…,p_{i-1}}*p_j
so that for dx in D_{p_0,…,p_{i-1}} and sx in
S_{base[0],…,base[i-1]} one has dx*d_i*g*s_i*sx*b_j = p_j
Search for dx, sx such that this equation holds for j = i;
it can be written as s_i*sx*b_j = J, dx*d_i*g*J = p_j
sx*b_j = s_i**-1*J; sx = trace(s_i**-1, S_{b_0,…,b_{i-1}})
dx**-1*p_j = d_i*g*J; dx = trace(d_i*g*J, D_{p_0,…,p_{i-1}})
h_n*b_j = p_j for all j, so that h_n is the solution.
Add the found (s, d, h) to TAB1.
At the end of the iteration sort TAB1 with respect to the h;
if there are two consecutive h in TAB1 which differ only for the
sign, the tensor is zero, so return 0;
if there are two consecutive h which are equal, keep only one.
Then stabilize the slot generators under i and the dummy generators
under p_i.
Assign TAB = TAB1 at the end of the iteration step.
At the end TAB contains a unique (s, d, h), since all the slots
of the tensor h have been fixed to have the minimum value according
to the symmetries. The algorithm returns h.
It is important that the slot BSGS has lexicographic minimal base,
otherwise there is an i which does not belong to the slot base
for which p_i is fixed by the dummy symmetry only, while i
is not invariant from the slot stabilizer, so p_i is not in
general the minimal value.
This algorithm differs slightly from the original algorithm [3]:
the canonical form is minimal lexicographically, and
the BSGS has minimal base under lexicographic order.
Equal tensors h are eliminated from TAB.
Returns size, res_base, res_gens BSGS for n tensors of different types
v is a sequence of (base_i, gens_i, free_i, sym_i)
where
base_i, gens_i BSGS of tensor of type i
free_i list of the fixed slots for each of the tensors
of type i; if there are n_i tensors of type i
and none of them have fixed slots, free = [[]]*n_i
sym 0 (1) if the tensors of type i (anti)commute among themselves
The graph is assumed to be unoriented and without
external lines.
Associate to each vertex of the graph a symmetric tensor with
number of indices equal to the degree of the vertex; indices
are contracted when they correspond to the same line of the graph.
The canonical form of the tensor gives a certificate for the graph.
This is not an efficient algorithm to get the certificate of a graph.