Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP

Loading…

SHADOWS #1508 - Don't commit #1498

Closed
wants to merge 218 commits into from
@smichr
Collaborator

Combinatorics #531 overhauled

I'm leaving this PR open as a shadow of #1508 which is against 0.7.2. That is the one that should be committed.

@smichr
Collaborator

I think the work on Polyhedron is ready to look at.

@jrioux jrioux commented on the diff
sympy/combinatorics/polyhedron.py
((39 lines not shown))
+ """
+ The constructor of the Polyhedron group object.
+
+ It takes up to three parameters: the corners, faces, and
+ allowed transformations.
+
+ The corners/vertices are entered as a list of arbitrary
+ expressions that are used to identify each vertex.
+
+ The faces are entered as a list of tuples of indices; a tuple
+ of indices identifies the vertices which define the face. They
+ should be entered in a cw or ccw order; they will be standardized
+ by reversal and rotation to be give the lowest lexical ordering.
+ If no faces are given then no edges will be computed.
+
+ >>> from sympy.combinatorics.polyhedron import Polyhedron
@jrioux Collaborator
jrioux added a note

No need to indent.

@smichr Collaborator
smichr added a note

I'm not to the proper examples, yet, and this is more parenthetical. Is it ok to leave it indented or do you still think it should dedent.

@jrioux Collaborator
jrioux added a note

It's fine, I guess.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
sympy/combinatorics/polyhedron.py
((284 lines not shown))
+ raise ValueError("Permutation size unequal to number of corners.")
+ if len(set([a.size for a in pgroups])) > 1:
+ raise ValueError("All permutations must be of the same size.")
+ obj._pgroups = pgroups
+ return obj
+
+ @property
+ def corners(self):
+ """
+ Get the corners of the Polyhedron.
+
+ The method ``vertices`` is an alias for ``corners``.
+
+ Examples
+ ========
+ >>> from sympy.combinatorics import Polyhedron
@jrioux Collaborator
jrioux added a note

There needs to be an empty line before the example.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
@jrioux
Collaborator

Each time you have

Examples
========

it should be followed by an empty line, then the actual code example.

@travisbot

This pull request passes (merged ed988314 into 5569bb2).

@Krastanov
Collaborator

SymPy Bot Summary: :eight_spoked_asterisk: All tests have passed.

Test command: setup.py test
master hash: 5569bb2
branch hash: ed988314ded03a202c8498d7de0657f285e51cd7

Interpreter 1: :eight_spoked_asterisk: All tests have passed.

Interpreter: /usr/local/bin/python2.5 (2.5.6-final-0)
Architecture: Linux (64-bit)
Cache: yes

Test results html report: http://reviews.sympy.org/report/agZzeW1weTNyDAsSBFRhc2sYirsjDA

Interpreter 2: :eight_spoked_asterisk: All tests have passed.

Interpreter: /usr/bin/python2.7 (2.7.3-candidate-2)
Architecture: Linux (64-bit)
Cache: yes

Test results html report: http://reviews.sympy.org/report/agZzeW1weTNyDAsSBFRhc2sYmNojDA

Interpreter 3: :eight_spoked_asterisk: All tests have passed.

Interpreter: /usr/bin/python3.2 (3.2.3-candidate-2)
Architecture: Linux (64-bit)
Cache: yes

Test results html report: http://reviews.sympy.org/report/agZzeW1weTNyDAsSBFRhc2sY8LYiDA

Build HTML Docs: :eight_spoked_asterisk: All tests have passed.

Docs build command: make html-errors
Sphinx version: 1.1.3

Test results html report: http://reviews.sympy.org/report/agZzeW1weTNyDAsSBFRhc2sYibsjDA

Automatic review by SymPy Bot.

@amakelov

I see that some of the stuff concerning cyclic forms in permutations.py is modified here. I'm currently working on excluding singleton cycles from the cyclic form. My approach is the following: I add a _size attribute to the Permutation class, and then, when faced with something like Permutation([[2, 3], [4, 5, 6], [8]]), find the maximum index appearing in the permutation (here it's 8) and assign the size of the permutation to that + 1. Then it remains to adjust some of the other methods in the class (after I adjusted mul so that it treats permutations of different sizes as if they leave all points outside their domain fixed, all the tests passed) so that they make sense with that new approach to cyclic forms. Is that going to be harmful to this PR?

@travisbot

This pull request fails (merged c46c2bb5 into 5569bb2).

@smichr
Collaborator

I see that some of the stuff concerning cyclic forms in permutations.py is modified here. I'm currently working on excluding singleton cycles from the cyclic form. My approach is the following: I add a _size attribute to the Permutation class, and then, when faced with something like Permutation([[2, 3], [4, 5, 6], [8]]), find the maximum index appearing in the permutation (here it's 8) and assign the size of the permutation to that + 1.

I thought of this, too, but the problem is that you don't know if they left out higher singletons (e.g. should [9] be in the permutation?)

Then it remains to adjust some of the other methods in the class (after I adjusted mul so that it treats permutations of different sizes as if they leave all points outside their domain fixed, all the tests passed) so that they make sense with that new approach to cyclic forms. Is that going to be harmful to this PR?

If you want to exclude them in the returned value of cyclic_form you can use the method reduced_cyclic_form.

If you have a 1-based cyclic_form you can use the function cyclic(cycle_form, n) to fill it out with singletons. I think I will modify it so you can optionally not subtract 1 from each element.

I wouldn't have been able to even talk about this 2 days ago, but I did a lot to combinaatorics over the past few days, so if you want to discuss this more it would be helpful.

@smichr
Collaborator

OK, so now you can convert a 0-based cyclic form into a 1-based cyclic form with or without singletons by using the one_based() function.

1-based -> 0-based-full with cyclic
0-based-full -> reduced-0-based-full with reduced_cyclic_form
0-based -> 1-based (full or reduced) with one_based.

@travisbot

This pull request passes (merged 0c94e217 into 5569bb2).

@amakelov

@smichr : the problem with higher singletons can be fixed in one of two ways:

  • as in my previous comment. We'll just have to make the functions treating two possibly different permutation allow different sizes. Additionally, we can supply an optional argument to the constructor that specifies the size of the permutation, something like a = Permutation([[2, 3, 4]], 20)
  • more ambitious: make a new class, ExtendedArrayForm or something, with a field _array_form that holds the usual array form of a permutation. Then we overload the __getitem__ method so that if the index is outside the bounds of self._array_form we return the index unchanged. Of course, we'll have to overload other things, like the __len__ and __str__ to make it behave like a list. Then instead of using a list to initialize the array form of a permutation, we use the corresponding ExtendedArrayForm. This will make all permutations behave as if they are acting on a practically infinite domain, and if we do it that way, we won't have to make any changes to the methods in Permutation - everything is going to work as expected, no casework like if len(a) > len(b),... will be needed. So this sounds like a rather elegant approach. On the other hand, I'm not entirely sure if it is possible to make it completely like a list, and also it doesn't seem like a very performance-efficient decision since ExtendedArrayForm instances will be created all the time.

And by the way, are we going to switch to 1-based form only? It's going to be hard and make the code uglier than it is right now; also, is it a good idea to have two standards for representing permutations -- isn't that going to get confusing?

@jrioux
Collaborator

While building docs:
Warning, treated as error:
doc/src/modules/combinatorics/permutations.rst:11: WARNING: autodoc can't import/find method 'sympy.combinatorics.permutations.perm_af_parity', it reported error: "perm_af_parity", please check your spelling and sys.path

@smichr
Collaborator

@jrioux , I got the sphinx working here and can check my own work now. The wrapping of the first See Also in partitions looks bad, however, as it appears to be trying to fill the paragraph instead of using ragged-right filling. And I added the LatticeOps change here to see that it would work in tests but will rebase this out once your request is in.

@smichr smichr closed this
@smichr smichr reopened this
@smichr
Collaborator

I did a total overhaul on #531. There are some other issues that were addressed along the way (e.g. quick_sort for LatticeOps args and dealing with evalf(1)) since these were causing test failures. I have already reviewed @saptman 's work but now need someone to look over the changes that I've made.

@smichr
Collaborator

@certik , you asked @saptman about whether this could be finalized...I worked with him quite a bit in review and discussion and so felt that I could do the final touches. It ended up being a lot more than that! (And I learned a lot in the process.) Perhaps you would have a chance to look this over?

@travisbot

This pull request passes (merged f73cd8a1 into 333ca3a).

@travisbot

This pull request passes (merged b86464d8 into a184841).

@travisbot

This pull request passes (merged 4b001a71 into 333ca3a).

@smichr
Collaborator

I'll close this here in favor of getting it in with 0.7.2 (#1508)

@smichr smichr closed this
@smichr smichr reopened this
@travisbot

This pull request passes (merged d0ed468f into d208118).

@travisbot

This pull request passes (merged e04e85e3 into d208118).

@wdjoyner

Is it possible to easily change the default printing of the elements of a perm gp to be in disjoint cycle notation?

If not, is is possible to describe in the docstring of perm gp exactly what the notation is. As I explained on the sympy list, group-theorists using this will expect disjoint cycle notation.

Is is possible to add lots more examples (eg, with the Rubik's cube group, etc) where the disjoint cycle notation is used to define a perm gp and then do lots of computations with it? For example, something like

F = Permutation([(17,19,24,22),(18,21,23,20),( 6,25,43,16),( 7,28,42,13),( 8,30,41,11)], size=49)
B = Permutation([(33,35,40,38),(34,37,39,36),( 3, 9,46,32),( 2,12,47,29),( 1,14,48,27)], size=49)
L = Permutation([( 9,11,16,14),(10,13,15,12),( 1,17,41,40),( 4,20,44,37),( 6,22,46,35)], size=49)
R = Permutation([ (25,27,32,30),(26,29,31,28),( 3,38,43,19),( 5,36,45,21),( 8,33,48,24)], size=49)
U = Permutation([ ( 1, 3, 8, 6),( 2, 5, 7, 4),( 9,33,25,17),(10,34,26,18),(11,35,27,19)], size=49)
D = Permutation([ (41,43,48,46),(42,45,47,44),(14,22,30,38),(15,23,31,39),(16,24,32,40)], size=49)
G = PermutationGroup([F,B,L,R,U,D])
G.order()
43252003274489856000

Generally, IMHO it seems to me that the documentation is minimal.

Sorry to seem so critical. This is a great contribution to sympy by Aleksander and Chris!

@travisbot

This pull request passes (merged a4c1bef1 into d208118).

@smichr
Collaborator
@wdjoyner
@smichr
Collaborator
@travisbot

This pull request passes (merged a1ab0336 into d208118).

@travisbot

This pull request passes (merged dd2c0434 into d208118).

@wdjoyner

This is much better now and, in my opinion, ready to go in.
Thanks Chris!

@smichr
Collaborator

There is now a global flag that controls output:

>>> a = Permutation([[1,2],[5,3]])
>>> a
Cycle(1, 2)(3, 5)
>>> isinstance(_, Permutation)
True
>>> Cycle(a)
[(1, 2), (3, 5)]
>>> Permutation.print_cyclic = False
>>> a
Permutation([0, 2, 1, 5, 4, 3])

So when you see Permutation or Cycle you know you are working with a
Permutation instance printing in two different ways. This is maybe a bad
idea since copying and pasting Cycle(1, 2)(3, 5) will not give a
Permutation, it will give a Cycle.

I got rid of the __mul__ method for cycle so only the Cycle(1, 2)(2, 3)
sort of notation is allowed (not Cycle(1, 2)*(2,3)).

The problem with the global flag is that now doctests have to explicitly
set the flag or else a previous test with the flag off will affect another
test that things that the flag is on (because that's what the default is).

I've got to check that all the PermutationGroups calculate properly when
they get permutations in cycle form.

@smichr
Collaborator

Should coset_decomposition fail if the input permutation is shorter than those in the PermutationGroup?


        >>> from sympy.combinatorics import Permutation, Cycle
        >>> Permutation.print_cyclic = True
        >>> from sympy.combinatorics.perm_groups import PermutationGroup
        >>> a = Permutation([[0, 1, 3, 7, 6, 4], [2, 5]])
        >>> b = Permutation([[0, 1, 3, 2], [4, 5, 7, 6]])
        >>> G = PermutationGroup([a, b])

        >>> c = Permutation([[2, 4], [3, 5]])
        >>> c.array_form
        [0, 1, 4, 5, 2, 3, 6, 7]
        >>> G.has_element(c)
        True
        >>> G.coset_decomposition(c)
        [[0, 1, 2, 3, 4, 5, 6, 7],
         [0, 1, 2, 3, 4, 5, 6, 7],
         [0, 1, 4, 5, 2, 3, 6, 7]]
        >>> Permutation.lmul(*[Permutation(p) for p in _]) == c
        True

This succeeds because the routine now adjusts input c to have the same length as the group and the result compares == because __eq__ ignores trivial tails of permutations (i.e. Permutation([1, 0, 3, 4, 5]) == Permutation([1, 0])). Does this seem right, David?

@asmeurer
Owner

Yes. We could make the default printing for all permutations to be the
cyclic_form. I know we used to have a flag for how polys were printed...I
thought we got rid of that and I don't recall why. Can you comment, Aaron?

Are you talking about the order keyword, because that still exists?

@smichr
Collaborator

I think I have a sane method of representation for disjoint cycle and array forms. If you look at the docstring for Permutation i try to lay it out clearly.

@smichr
Collaborator

about the order keyword

Yes...I never use it and I know that Mateusz stripped something out at one point. Thanks for clarifying.

@travisbot

This pull request fails (merged 4b81be1f into d208118).

@travisbot

This pull request fails (merged 17d12a16 into d208118).

@wdjoyner
@travisbot

This pull request passes (merged 22607286 into d208118).

@travisbot

This pull request passes (merged a113079f into d208118).

@travisbot

This pull request fails (merged 79498c27 into d208118).

@travisbot

This pull request passes (merged 8bfba19c into d208118).

@travisbot

This pull request passes (merged 533d41ee into d208118).

@smichr
Collaborator

Does it makes sense to allow duplicate permutations in a PermutationGroup? I'm thinking not, and have made the changes accordingly.

@travisbot

This pull request fails (merged 17203029 into d208118).

@travisbot

This pull request fails (merged fdee9636 into d208118).

@wdjoyner
@pernici

_af_mul is slower than perm_af_muln previously defined; on my computer computing the order of
the Rubik cube takes 1.84s using _af_mul; using the code of perm_af_muln it takes 1.6s, and taking away
the check that Permutations have the same size, as in _af_mul in this PR (which does not seem to me to be
a good idea), it takes 1.54s; in particular _af_mul takes half the time as the one in this PR.

@pernici
    def __mul__(self, other):
        """
        Routine for multiplication of permutations following R to L order.

        Note
        ====

        a*b*c applies permutations in order c, b, a which is consistent with
        function notation abc(x) meaning a(b(c(x))).

I do not think this is correct; it is lmul which is consistent with the function notation

For instance

>>> from sympy.combinatorics import Permutation
>>> Permutation.print_cyclic = False
>>> p = Permutation([1, 2, 3, 0])
>>> q = Permutation([3, 2, 0, 1])
>>> Permutation.lmul(p, q)
Permutation([0, 3, 1, 2])
>>> [p.array_form[q.array_form[i]] for i in range(4)]              # p(q(x))
[0, 3, 1, 2]
>>> p*q
Permutation([2, 0, 1])

p*q is consistent with the exponential notation `x(p*q) = (xp)**q

@smichr
Collaborator

oops, you have to escape math or else the ** gets interpreted as bold.

Regarding L to R or R to L, we can ask "did p get applied
to q or vice versa. If p gets applied to q we take position 1 from q (which is 2)
then position 2 from q (0), etc... to obtain [2, 0, 1, 3]. This is R to L
order. If q gets applied to p then that is L to R order (which is what lmul
does. If you still think I am incorrect about this, please let me know. It took a long
time to convince myself of the order (and thinking about 3 permutations
rather than 2 helped to clarify the issue).

>>> p = Permutation([1, 2, 3, 0])
>>> q = Permutation([3, 2, 0, 1])
>>> r = Permutation([3, 1, 0, 2])
>>> p*q*r
Permutation(1, 3, 2)
>>> r*q*p
Permutation(0, 2)(1, 3)
>>> Permutation.lmul(p,q,r)
Permutation(0, 2)(1, 3)

p*q*r will apply q to r then p to that result whereas lmul(p, q, r) applies
q to *p* then r to that result: permutation are applied in the order
listed, from left to right, and so it is called lmul.

If you write [a[i] for i in b] you are applying b to a so

[p.array_form[q.array_form[i]] for i in range(4)]              # p(q(x))

is applying q to p: q's elements are selcting p's elements, so I would write q(p) (and I would write the code as

[P.array_form(qi) for qi in q.array_form]
@smichr
Collaborator

@asmeurer and all others. I think this is ready to go. I only wish I could discuss groups and cosets better. Also, there is the issue about the possible name change for coset_repr -> coset_decomposition and coset_decomposition->coset_factor. @wdjoyner or @pernici or @amakelov , do you have feedback about that name change?

@smichr
Collaborator

@pernici , I split _af_mul into _af_muln -- this should restore the performance.

@smichr
Collaborator

One further question: should we get rid of the space between elements so Permutation(1, 2, 3) shows as Permutation(1,2,3)?

@wdjoyner
@wdjoyner
@smichr
Collaborator
@smichr
Collaborator
@pernici

I split _af_mul into _af_muln -- this should restore the performance.

No, it does not; computing the Rubik cube order takes the same time as before.
_af_muln with 7 arguments makes 6 lists instead of one; using master only one list is made;
this is why it is faster.

@wdjoyner
@pernici

In the documentation of __mul__ it is written

        a*b*c applies permutations in order c, b, a which is consistent with
        function notation abc(x) meaning a(b(c(x))).

This is wrong; I quote here Bachmann in https://groups.google.com/forum/?fromgroups=#!topic/sympy/4ZzZxFbRIAo

Let us fix a set X we are considering the permutation group of, below I
will take X = {1, 2, 3, 4, 5}. A permutation of X is by definition a
bijective function f:X->X. It is specified uniquely by providing the
image of every element. We can write this in the short form
[f(1), f(2), f(3), f(4), f(5)]. In this way, every permutation is
represented by an array of constant size.
...

Now let's consider composition. There are two schools of thought. Let me
write * for "ordinary" (where I come from) composition, and . for
"weird" composition. By definition, if f, g are permutations, then the
permutation fg is the unique mapping such that (fg)(x) = f(g(x))
["apply right to left"], whereas (f.g)(x) = g(f(x)) ["apply left to
right"]. [*]

[] Note that this can be made to look more sensible (for some
definitions of sensible), by writing *arguments
on the left: applying f
to x can be written as (x)f, and then (x)(f.g) = ((x)f)g ...
<\quote>

(x)f is sometimes called exponential notation x**f.
This is the convention used in this PR.

@pernici

In the example you give

```>>> from sympy.combinatorics import Permutation

Permutation.print_cyclic = False
p = Permutation([1, 2, 3, 0])
q = Permutation([3, 2, 0, 1])
r = Permutation([3, 1, 0, 2])
pqr
Permutation([0, 3, 1, 2])
rqp
Permutation([2, 3, 0, 1])
[p.array_form[q.array_form[r.array_form[i]]] for i in range(4)]
[2, 3, 0, 1]

(x)(pqr) = (p(x))(qr) = (q(p(x)))*r = r(q(p(x)))

r(q(p(x))) is given by

>>> [r.array_form[q.array_form[p.array_form[i]]] for i in range(4)]
[0, 3, 1, 2]

You write

pqr will apply q to r then p to that result whereas lmul(p, q, r) applies
q to p then r to that result: permutation are applied in the order
listed, from left to right, and so it is called lmu

I do not think it is a good terminology to say that q is applied to r; q is applied to i in [0,1,..,n-1]
not to another permutation; at least in the sense of p*q, one can apply it to q as p**q == p.conjugate(q).

I think that the definition of p.conjugate(q) should be ~q*p*q, not as defined in the code, since

((x)p)q = (x)p*q = (x)q*~q*p*q = (x)q*p.conjugate(q)

@pernici

I do not think matrix_form is right; it is the Cauchy two-line form, so that

>>> a = Permutation([2, 0, 3, 1])
>>> a.matrix_form
[0, 1, 2, 3]
[2, 0, 3, 1]

not [1, 3, 0, 2].

@pernici

In the dostring of Permutation it is written

Any permutation can be represented as a set of transpositions

it should be as a sequence of transpositions, since the order matters.

@smichr
Collaborator
@smichr
Collaborator
@wdjoyner
@smichr
Collaborator

regarding the terminology for multiplication:

Do you have a recommendation for how to say this? Is "r selects from q and
the result selects from p" for p*q*r better?

Regarding mul and lmul. I could very well be wrong about the terminology
being used but this much is sure:

The code that uses lmul is applying permutations in the opposite order of
__mul__. So whatever order we describe for a*b*c, the opposite applies
to lmul(a, b, c).

@smichr
Collaborator

array_form is already taken for the explicit permutation so cauchy_form would be better.

@smichr
Collaborator

OK, _af_mul is almost back to master...I don't like the recursive call, though...how is the performance now?

@pernici

OK, _af_mul is almost back to master...I don't like the recursive call, though...how is the performance now?

It is fine for the Rubik cube, since there are at most products of 7 permutations; for more than 8 permutations
it is still slow without the recursion; why do you not like it?

@pernici

In http://www.sagemath.org/doc/reference/sage/combinat/permutation.html
multiplication is done by default from left to right 'l2r' (like in GAP)

mult: ‘l2r’ - the multiplication of permutations is done like
composition of functions from left to right.
That is, if we think of the permutations p1 and p2 as functions,
then (p1*p2)(x) = p2(p1(x)). This is the default in multiplication in GAP.

‘r2l’ - the multiplication of permutations is done right to left so that (p1*p2)(x) = p1(p2(x))

Maybe we can use a similar terminology:
'l2r' L to R convention
'r2l' R to L convention

so that __mul__ satisfies the L to R convention
lmul satisfies the R to L convention

Example:

>>> from sympy.combinatorics import Permutation
>>> p = Permutation([1, 2, 3, 0])
>>> q = Permutation([3, 2, 0, 1])
>>> r = Permutation([3, 1, 0, 2])
>>> for i in range(4):
...     assert Permutation.lmul(p,q,r)(i) == p(q(r(i)))
...     assert (p*q*r)(i) == r(q(p(i)))
...
>>>

In the following I suggest changes concerning multiplication convention

def _af_mul(a, b):
    """
    Product of two permutations in array form, following the
-    L to R convention: given args A and B, B is applied to A.
+   R to L convention:  _af_mul(a, b)[i] = a[b[i]]

def _af_muln(*a, **kwargs):
    """
    Product of two or more permutations in array form, following the
-   L to R convention: given args [A, B, C], B is applied to A and
-   then C is applied to the result.
+   R to L convention:   _af_mul(a, b, c)[i] = a[b[c[i]]]

In the docstring of Permutation

-    The product of two permutations p and q is defined as their composition as
-    functions, (p*q)(i) = p(q(i)) [6]_.

+    The product of two permutations p and q is defined using the
+    L to R convention (p*q)(i) = q(p(i)); (p*q*r)(i) = r(q(p(i)))

    @staticmethod
    def lmul(*args):
        """
-        Return product of permutations following L to R order.
-        Given lmul(A, B, C), A is applied to B and that result
-        is applied to C. This is the opposite of A*B*C which
-        applies B to C then applies A to that result.

+        Return product of permutations following R to L order
+        Permutation.lmul(A, B, C)(i) == A*B(C(i)))

    P*foo was changed to lmul(P, foo):

maybe foo*P ? I do not understand. Is this line necessary?

    def __mul__(self, other):
        """
-        Routine for multiplication of permutations following R to L order.
+        Routine for multiplication of permutations following L to R order.

        Note
        ====

-        a*b*c applies permutations in order c, b, a which is consistent with
-        function notation abc(x) meaning a(b(c(x))).

+        (a*b*c)(x) means c(b(a(x)))

maybe transpositions should return the cycles in opposite order,
so that their product (not the lmul product) is equal to the permutation?

    def conjugate(self, x):
        """
-       Computes the conjugate Permutation ``x*p*~x``
+       Computes the conjugate Permutation ``~x*p*x`
@smichr
Collaborator
@smichr
Collaborator
@scolobb scolobb immutablematrices.rst: Doctest the TypeError.
This commit makes the doctest in the said file actually test the raising
of ``TypeError``.
98a3f3f
@smichr
Collaborator

@pernici , what do you think about the re-written docstrings for the different mul methods?

I've added ^ as an operator for conjugate -- does that seem ok to you guys?

Interestingly, this whole PR was named something like "allow permutation to be indexed". I never implemented that because it is ambiguous whether p[2] should give element at index 2 or the 2nd ranked permutation. The former is already implemented by __call__ and the latter depends on what type of ranking scheme one uses, so __getitem__ was never defined.

@pernici , do you want this to go in first or do you want yours to go in and then I rebase over it?

@smichr smichr referenced this pull request
Merged

Matrix docs fix examples #1525

@pernici

_af_rmuln is still slow, compared to perm_af_muln; adding the latter here

In [1]: from sympy.combinatorics.permutations import Permutation, _af_rmuln, perm_af_muln

In [2]: a = [Permutation.unrank_lex(500, i).array_form for i in range(10)]

In [3]: timeit _af_rmuln(*a)
1000 loops, best of 3: 626 us per loop

In [4]: timeit perm_af_muln(*a)
1000 loops, best of 3: 188 us per loop
@pernici

In the Cycle doctest

        >>> a = C(1, 2)
-       >>> print a(2, 3)
+       >>> a(2, 3)

In the Permutation docstring what does the following mean?

 If you choose to number items
    starting from 1 (and thus enter the permutation as [1, 2, 4, 3]) SymPy
    will pretend that the 0th element was not moved and store the permutation
    as [0, 1, 2, 4, 3].

I do not undestand the following, since (1, 2)(1, 3)(2, 3) is not a
disjoint cycle product

    It also provides some economy in entry when computing products of
    permutations that are written in disjoint cycle notation:

    >>> Permutation(1, 2)(1, 3)(2, 3)
    Permutation([0, 3, 2, 1])
    >>> _ == Permutation([[1, 2]])*Permutation([[1, 3]])*Permutation([[2, 3]])
    True

In the Permutation doctest I would take away the paragraphs on
Cauchy Matrix Notation and 2-Line Notation and Matrix Notation;
cauchy_form is still wrong, it is not the Cauchy 2-line notation;
I suggest take it away.

@pernici
    The permutation can act as a bijective function, telling what element is
    located at a given position

    >>> p.array_form
    [1, 0, 2, 3]
    >>> p.array_form.index(1) # the hard way
    0
    >>> p(1) # the easy way
    0

This is wrong; here is a counterexample

>>> p = Permutation([5,2,3,4,1,0])
>>> p.array_form.index(1)
4
>>> p(1)
2
@pernici
The product of two permutations p and q is defined as their composition as
functions, (p*q)(i) = p(q(i)) [6]_.

This is still wrong; the correct expression is (p*q)(i) = q(p(i))
In the example which follows that statement, q commutes with p so that it is not a good example;
here is a possible replacement

>>> p = Permutation([1, 0, 2, 3])
>>> q = Permutation([2, 3, 1, 0])
>>> list(p*q)
[3, 2, 1, 0]
>>> [q(p(i)) for i in range(p.size)]
[3, 2, 1, 0]
@mrocklin mrocklin Update immutablematrix.__setitem__ error message
Now points user to use the Matrix class for this case.
a2c5418
@asmeurer
Owner

Check that ^ has the right precedence for what you want.

@smichr
Collaborator

q(p(i))

I made that change - thanks for the example, too. I had caught that commuting pair elsewhere but not here.

check ^ precedence

>>> p = Permutation([1, 0, 2, 3])
>>> q = Permutation([2, 3, 1, 0])
>>> p^q==p.conjugate(q)
True
>>> p^q != q^p
True
@smichr
Collaborator

.index(1)

.index(1) is replaced with [1] and the example you gave has been used.

@smichr
Collaborator

print C

the print has been removed

take it away

The docstring and function for cauchy_form have been removed.

@smichr
Collaborator

what does the following mean

I removed that and just left it at "Python is 0-based". It means that if you type Permutation(1,2) you are swapping positions 1 and 2 since SymPy is assuming that the array form starts at 0.

I do not undestand the following, since (1, 2)(1, 3)(2, 3) is not a
disjoint cycle product

It is just a product of disjont cycles (in this case a series of transpositions). is there something to make clearer yet?

@smichr smichr referenced this pull request
Closed

0.7.2 Release #1507

@smichr
Collaborator

_af_rmul is slow

OK, I restored the original form. My only concern is that I don't see why this is being broken down in a binary way instead of just being handled in 7 piece chunks at a time.

@pernici

It is just a product of disjoint cycles (in this case a series of transpositions). is there something to make clearer yet?

It if fine.

@asmeurer
Owner

No, what I meant is that a^b*c is going to be interpreted as a^(b*c). Is this OK? Note that this is the opposite of how ** would be interpreted.

@pernici

a^b*c = a^(b*c) seems to me to be OK.
However if the rule is a^b^c = a^(b^c)
then a^b^c = b*c*~b*a*(b*~c*~b) which has no meaning, while
(a^b)^c = c*(b*a*~b)*~c = a^(c*b) makes sense, in fact defining R(x)y = y^x = x*y*~x one gets
R(c)R(b)a = R(c*b)a

@wdjoyner
@rlamy rlamy Move int_tested to the core and refactor it.
* Move int_tested() to sympy.core.compatibility, to make it
  importable anywhere.
* Simplify it to take only a single argument, like int().
* Rename it as_int().
eb6e9da
@wdjoyner
@pernici

To have (a.conjugate(b)).conjugate(c) = a.conjugate(b*c) it should be a.conjugate(b) = ~b*a*b
while currently a.conjugate(b) = b*a*~b leading to (a.conjugate(b)).conjugate(c) = a.conjugate(c*b)
In the first case R(x)y = x.conjugate(y) gives R(c)R(b)a = R(b*c)a, in the second case R(c)R(b)a = R(c*b)a;
since the second case is an homomorphism it seems nicer.

I cannot find which are the precedence rules in Python for __xor__;
is a^b^c equal to (a^b)^c or a^(b^c) or it is not defined?

The following example shows that if it is defined, it is a^b^c =(a^b)^c`

class A:
  def __init__(self, a):
    self.a = a

  def __str__(self):
    return str(self.a)

  def __xor__(self, other):
    return A('(%s^%s)' % (self.a, other.a))

a = A('a')
b = A('b')
c = A('c')

print a^b^c   # prints ((a^b)^c)

If it is so, it has the same precedence rule as conjugate.

@asmeurer
Owner

I believe all binary operators in Python associate from the left, with the exception of **.

@smichr
Collaborator

On Sat, Sep 8, 2012 at 12:57 AM, pernici notifications@github.com wrote:
a^bc = a^(bc) seems to me to be OK.
However if the rule is a^b^c = a^(b^c)
then a^b^c = bc~ba(b~c~b) which has no meaning, while
(a^b)^c = c(ba~b)~c = a^(cb) makes sense, in fact defining R(x)y = y^x = xy*~x one gets

It is left binding as Aaron has said:

>>> q^p^r
Permutation(1, 2)
>>> q^(p^r)
Permutation(2)(0, 1)
>>> (q^p)^r
Permutation(1, 2)
>>> q,p,r
(Permutation(2)(0, 1), Permutation(2), Permutation(0, 2))
@smichr
Collaborator

So is everything ok with the conjugate operator? (And nice demo with classes, @pernici)

@smichr smichr Merge pull request #1526 from rlamy/int_tested
Move int_tested to the core and refactor it.
a0e2c05
@pernici

wdjoyner wrote

I think conjugation should be defined so that (a.conjugate(b)).conjugate(c) = a.conjugate(b*c) and (a^b)^c = a^(bc)

With the current definition a.conjugate(b) = b*a*~b one has instead (a^b)^c = a^(c*b)

>>> from sympy.combinatorics import Permutation
>>> q= Permutation(6, 9, 8)
>>> p= Permutation(6, 9)
>>> r= Permutation(6, 9, 7, 8)
>>> q^p^r
Permutation(9)(6, 8, 7)
>>> q^(p*r)
Permutation(7, 8, 9)
>>> q^(r*p)
Permutation(9)(6, 8, 7)

I suspect that Joyner is right;
I wrote before that currently a.conjugate(b) = ba~b leading to (a.conjugate(b)).conjugate(c) = a.conjugate(cb)
and that definingR(x)y = x.conjugate(y) one gets
`R(c)R(b)a = R(c
b)a;
this is how usually homomorphisms are defined, but
R(c)R(b)is a product of application defined in the
usual order, while
cbis the permutation product defined in the standard (crazy, in my opinion) way,
so a homomorphism is
R(c)R(b) = R(b
c)`.

Another argument is that it should be, using the notation x -> (x)p
((x)p)q = (x)p*q = (x)q*~q*p*q = (x)q*p.conjugate(q) so p.conjugate(q) = ~q*p*q

Therefore the correct definition of conjugate is a.conjugate(b) = ~b*a*b leading to (a^b)^c = a^(b*c)

Another issue:
is there a problem in using ^ for conjugate when SymPy is called in Sage, were ^ is the exponential operator,
with different precedence rules?

@smichr
Collaborator

Therefore the correct definition of conjugate is a.conjugate(b) = ~bab

If a.conjugate(b) is defined as ~a*b*a then the following will not hold (just quoting from the docstring; I don't know how things should be):

        >>> p = Permutation([2,0,3,1])
        >>> q = Permutation([1,0,3,2])
        >>> r = Permutation([0,2,3,1])
        >>> p**4
        Permutation([0, 1, 2, 3])
        >>> p**q == p.conjugate(q)
        True
        >>> q**p == q.conjugate(p)
        True
        >>> (p**q)**r == p**(r*q)
        True

I agree with @pernici that the way we have the __mul__ defined right now is very confusing. I just read www.neiu.edu/~bhdayton/permutations.pdf where it says "when we write gf it means we do f first and g after" but in sympy we would have to type this as f*q and that's terrible. Let's get this settled. If standard notation for g*f in permutations means "f first and g next" then we have to reverse mul so we can just write that. So...is __mul__ backward right now?

@pernici

p**q has the wrong precedence,
in the sense that p**q**r = p**(q**r)
using p**q = p.conjugate(q) has no particular meaning;
therefore conjugate should be taken away from __pow__.

In www.neiu.edu/~bhdayton/permutations.pdf it is used the same convention
for permutations as in master; the convention used in this PR is
the standard group theory software, in particular it is the one used in GAP,
and by default in Sage; it is also used by most books on permutations.

I think this is the reason why __mul__ has been redefined in this PR.

For instance in Holt,Eick,O'Brein
"Handbook of Computational Group Theory", from which I quote:

For x in \Omega and g in Sym(\Omega), we shall denote the result of applying
g to x by x^g rather than by the more usual g(x).
The composite gh will mean "first apply g, then apply h", and so
x^(gh)=(x^g)^h.

To be precise I wrote in the above x^g for what is visualized
by latex $x^{g}$. This is what I wrote as (x)g in the previous post,
with ((x)p)q = (x)p*q = (x)q*~q*p*q = (x)q*p.conjugate(q).

Using '^' this equation becomes
x^p^q = (x^p)^q = x^q*p^q defining p^q = p.conjugate(q) = ~q*p*q

One can introduce also x^p, where x is an integer
or a list of elements, using __rxor__;

Here is an example, after redefining p.conjugate(q) = ~q*p*q
and (to be improved)

    def __rxor__(self, x):
        if int(x) == x:
            return self(x)
        else:
            raise NotImplementedError
>>> from sympy.combinatorics import Permutation
>>> p = Permutation(1, 2, 9)
>>> q = Permutation(6, 9, 8)
>>> r = Permutation(9)(4,6,8)
>>> q^p^r
Permutation(9)(1, 4, 8)
>>> (q^p)^r
Permutation(9)(1, 4, 8)
>>> q^(p*r)
Permutation(9)(1, 4, 8)
>>> 2^p
9
>>> 2^p^q
8
>>> 2^(p*q)
8


sage -gap
> p := (2,3,10);
> q := (7,10,9);
> r := (5,7,9);
gap> q^p^r;
Syntax error: '^' is not associative
> (q^p)^r;
(2,5,9)
gap> 3^p;
10
gap> (3^p)^q;
9
gap> 3^(p*q);
9

Notice that in GAP one must specify the parenthesis (q^p)^r

@smichr
Collaborator

@pernici , could you just make a pull request against this PR implementing the changes?

@pernici

could you tell me the procedure to make a pull request against this PR?

@smichr
Collaborator

It's just like making a regular pull request except after you press the pull request button you must change the branch against which you want it applied. The default is sympy master. Just select from the list smichr (base repo) and combinatorics (base branch) then continue as usual.

@pernici

Thanks; I have another question about this.
Currently to get your branch combinatorics I download the zipped branch each time; since this is done out
of my git repository, to do a pull request out of it I have to copy the files I am interested in in a branch of mine,
make the changes and then do the pull request; this is messy.
There is surely a way to get your branch in my repository. I guess that it is roughly

git remote add smichr https://github.com/smichr/sympy
git fetch smichr
git checkout smichr/combinatorics

but I do not dare to do that because I am afraid I would get all your ( > 600) branches in my repository,
which would make the command git branch unreadable, and I do not know what happens if a branch of
yours has the same name as one of mine; besides, 600 branches would take a lot of memory.

Can you tell me if the above commands are correct and that what I am afraid of does not happen?

@matthew-brett

There is surely a way to get your branch in my repository. I guess that it is roughly

git remote add smichr https://github.com/smichr/sympy
git fetch smichr
git checkout smichr/combinatorics

The last would more likely be something like:

git checkout -b my-combinatorics smichr/combinatorics

but I do not dare to do that because I am afraid I would get all your ( > 600) branches in my repository,
which would make the command git branch unreadable,

Well, git branch -a would show all of smichr's branches, but not
git branch`

and I do not know what happens if a branch of
yours has the same name as one of mine;

smichr's branches only appear as 'remote' branches, with prefix
'smichr/' so they won't clash with yours. So, if you have a
'my-fixes' branch, which perhaps you push to your own github repo,
meaning you have a remote branch `origin/my-fixes' - there would be no
clash with 'smichr/my-fixes' - for example.

besides, 600 branches would take a lot of memory.

Don't forget that a branch is just a pointer to a commit. The amount
of disk space taken up by the fetch, over and above the sympy original
checkout, is the amount of code that is in smichr's branches and not
in the sympy original checkout branches, and that's likely to be a
relatively small amount, because a lot of it will have been merged
over time.

@pernici

Thanks!

@smichr
Collaborator

@wdjoyner last commented that he thought this was ready to go. I took off the __xor__ commit so that can be settled out as a new PR. I also rewrote the docstring for commutator to accurately reflect what it returns.

This then, as far as I am concerned, is ready to go but should be committed from #1508, not here. @pernici , do you have any estimate of how long it will take to resolve the conjugate issue? I would imagine this would be an important change to have be part of 0.7.2 if there is a change in what it returns.

@smichr
Collaborator

git checkout -b my-combinatorics smichr/combinatorics

Or, realizing that "Namespaces are one honking great idea -- let's do more
of those!" (last line from "import this") you don't have to try avoid a
clash with the "my-" prefix.

git checkout -b combinatorics smichr/combinatorics

is fine, too.

@pernici

do you have any estimate of how long it will take to resolve the conjugate issue?

I have made a pull request for it.

@smichr smichr Merge pull request #1528 from jrioux/documentation
Fix Sphinx doc failure in 0.7.2 branch.
9f56be7
@pernici

I have the smichr/combinatorics in my repository; how do I update it to the last changes you made?

@jrioux
Collaborator

git fetch smichr

@smichr
Collaborator
saptman and others added some commits
@saptman saptman combinatorics/permutations: Made the permutation object indexable.
Signed-off-by: Saptarshi Mandal <sapta.iitkgp@gmail.com>
ce5d49c
@saptman saptman combinatorics/polyhedron: Added a Polyhedron class.
Signed-off-by: Saptarshi Mandal <sapta.iitkgp@gmail.com>
eedb639
@saptman saptman combinatorics/polyhedron: Added tests for Polyhedron.
Signed-off-by: Saptarshi Mandal <sapta.iitkgp@gmail.com>
0688428
@saptman saptman combinatorics/polyhedron: Added tests for make_perm.
Signed-off-by: Saptarshi Mandal <sapta.iitkgp@gmail.com>
e9f04f4
@saptman saptman combinatorics/polyhedron: Fixed the doctest to make things a bit
more clear.

Signed-off-by: Saptarshi Mandal <sapta.iitkgp@gmail.com>
f94b1bd
@saptman saptman combinatorics/polyhedron: Made the test code a bit more
streamlined.

We use a set in edge list construction.

Fixed some doctests comments.

Signed-off-by: Saptarshi Mandal <sapta.iitkgp@gmail.com>
1642d44
@saptman saptman combinatorics/polyhedron: Made the docstrings a bit more clearer.
Helped-by: Chris Smith <smichr@gmail.com>
Signed-off-by: Saptarshi Mandal <sapta.iitkgp@gmail.com>
f9e8b6f
@saptman saptman combinatorics/polyhedron: Simplified docstring and added a
reference.

Signed-off-by: Saptarshi Mandal <sapta.iitkgp@gmail.com>
29209f5
@smichr smichr polyhedron fixes e368ca3
@smichr smichr polyhedron: add test for args 61a9149
@smichr smichr polyhedron: use FiniteSet and Symbols d271b6c
@smichr smichr polyhedron: fix randomization test 7876322
@smichr smichr polyhedron: docstring and method changes d85515c
@smichr smichr add predefined corners and faces for standard solids 3f8200e
@smichr smichr polyhedron: fix and test predefined polyhedra 10d5ac0
@smichr smichr polyhedron: make faces canonical 55b40ee
@smichr smichr partition: use group in as_dict f8c7a57
@smichr smichr remove indexing of permutation; use array_form instead 7288ecd
@smichr smichr polyhedron: add polyhedra nets in docstring 1c4c854
@smichr smichr polyhedron: use list when using index for Py 2.5 3626b4c
@smichr smichr polyhedron: fix docstrings 1883e99
@smichr smichr iterables: has_variety added and used throughout sympy 9f7053e
@smichr smichr combinatorics changes
Transpositions are now stored in FiniteSet

Permutation's cyclic_form is made canonical with singletons
(which are insignificant but necessary for the current implementation)
are stored at the end of the list.
c009ce9
@smichr smichr permutations: add support method c77439b
@smichr smichr fix named_groups 5f6e30c
@smichr smichr combinatorics: cleanup
The utility functions for working with lists instead of permutations
(perm_af_*) were renamed (_af_*) to make them shorter and private.

The _af_mul routine was simplified to not be nested.
995a56e
@smichr smichr permutations: add one_based 9221943
@smichr smichr fix reduced_cyclic_form 4f554bc
@smichr smichr combinatorics: documentation changes 7ca0fa7
@smichr smichr combinatorics: add doc files 39e8e7d
@smichr smichr combinatorics: fix documentation dc3a0a2
@smichr smichr fix LatticeOps args
Since args need only be intra-session canonical, hash sorting is
used than the more elaborate default_sort_key sorting.

If an inter-system compatibility is needed then a _sorted_args
routine can be written using the default_sort_key sorting.
e8c2f60
@smichr smichr permutations: further simplify _af_mul 25b8ba5
@smichr smichr fix perm_groups 23fad87
@smichr smichr permutations: one_based omits singletons by default 890a178
@smichr smichr fix DirectProduct doc error e0c3a42
@smichr smichr combinatorics: perm_groups edit fb43ed1
@smichr smichr allow evalf(1) fb0d419
@smichr smichr add quick_sort for args ordering
When an object has arguments whose order is arbitrary, it is
still a good idea to order them in some canonical way so that
two objects that may have been entered with arguments in different
order will still be the same, e.g. x + 2 == 2 + x. Although hash
sorting is used (in Add, for example) this may run into hash collisions
and so in those cases there should be a way to consistently break ties.
The quick_sort function has been added to iterables to allow for this.
It sorts elements of an iterable on the basis of hash and breaks ties
using the default_sort_key. A tuple is returned from the function.
5f1c551
@smichr smichr fix quick_sort doctest 3c49022
@smichr smichr Polyhedron: cyclic clash; polyhedra have pgroups; Basic.copy
named_groups uses cyclic so the function in permutations was
changed to full_cyclic_form0 and one_based was changed to
cyclic_form1; these are more descriptive and consistent with
the method name (cyclic_form).

All pre-defined polyhedra come with pgroups that are checked
to cover all orientations.

Docstrings have been expanded.

The _canonical function was renamed minlex but it was kept
in the polyhedron file; it is potentially useful for iterables
or geometry.

Unused imports were deleted.

The copy method was added to Basic to allow a copy to be
made of an object that implements in-place changes.
052f6fe
@smichr smichr code printing: use repr(expr); preprosessor (sp)
correction brought to attention by

David M. Rogers <predictivestatmech@gmail.com
1b60e43
@smichr smichr move minlex to iterables; combinatorics edits 6fbad64
@smichr smichr add tests for quick_sort 0de8dbc
@smichr smichr permutations: add tests and docstrings about R to L Mul: ABC(x) = A(B…
…(C(x)))
dac9403
@smichr smichr Permutation.mul added for convenience; product can be reversed 4512247
@smichr smichr variations requires n f2f027c
@smichr smichr permutation: add 0 to non-0-based perm
A duplicate __new__ method was also deleted from Permutation.
26d61b4
@smichr smichr permutations: keep [] bf2c098
@smichr smichr permutations: drop singletons from cyclic_form 5c63a71
@smichr smichr permutations: add DisjointCycle class ff5a323
@smichr smichr import has_dups 79f9ee2
@smichr smichr import Tuple 82f0f28
@smichr smichr add note about L->R processing of DisjointCycle 9c84979
@smichr smichr permutations: add as_cycle method 2a57a3d
@smichr smichr permutations: null DisjointCycle and has_dups err; length/support met…
…hods

length and support are methods since they do not simply read a property,
they calculate something. (It's an O(n) process, though, so perhaps they
could remain properties.)
2c7b8a9
@smichr smichr permutation: clean up DisjointCycle
Thanks to Tom Bachmann for the help in cleaning this up.

Also, dict is used instead of defaultdict since it, too, allows
for a __missing__ method. The printing of the DisjointCycle
as something other than a dict in doctests has not been resolved,
i.e. in doctests

>>> DisjointCycle(1,2)
{1: 2, 2: 1}

while during an interactive session this would give [(1, 2)].
a383604
@smichr smichr permutations: use lmul in code b2d5595
@smichr smichr permutation: lmul and other edits; DisjointCycle -> Cycle
Cycle and Permutation both act from R to L in the sense that
given A and B, A*B applies B to the elements and then applies A.

_af_mul works from L to R (but has a reverse option)
lmul accepts a list of permutations and processes them L to R

lmul is used throughout combinatorics to clearly indicate which
order the multiplication is being performed, but it could probably
be removed and the arguments just reversed in order.

A current deficiency with Permutation is that it cannot be rebuilt
from its args when it is in cycle form since the entire cycle is
not stored. A way around this would be to store the entire cycle
but have the print method only show the non-singletons.
663b9c9
@smichr smichr remove as_cycle; use Cycle(p) instead
Cycle will always know how to instantiate from a permutation
and such a cycle will always be convertable back to a permutation
since all elements are present; the general Cycle, however, cannot
be converted without a size parameter if some elements are missing.

Permutation(Cycle(p)) will return p as a permutation in array form
Permutation(Cycle(1, 6)) will fail
Permutation(Cycle(1, 6), size = 7) will succeed
1dd6c1b
@smichr smichr permutation: store args as array form 9c053ed
@smichr smichr permutation: add __rmul__ and do not coerce in lmul e184f79
@smichr smichr polyhedron: array_form and cyclic_form added; Perm used internally 4bb77bf
@smichr smichr perm_groups: __getitem__ added; generators stored as list a8d78a8
@smichr smichr permutation: pgroups->pgroup; docstring edits 90c4b26
@smichr smichr perm_group gets len; watches for single Permutation 72c6c05
@smichr smichr polyhedron: store pgroup as PermutationGroup 0802f47
@smichr smichr polyhedron: pgroups -> pgroup 8df3e36
@smichr smichr polyhedron: use list for Py 2.5 sake ac07c29
@smichr smichr iterables: runs added 13063ce
@smichr smichr quick_sort updated for failing test 47dc462
@smichr smichr permutations: coverage to 100% and other changes
Misc changes other than docstring edits and test addition:

no testing in the _af_foo routines to allow them
to be as fast as possible (as private methods)

__invert__ uses duplicating _af_invert now

Permutations support the iter method so list(P)
will give P.array_form

error corrected in parity which used the _cyclic_form
which is now not inclusive of singletons

the code of runs was simplified and moved to iterables

the docstring of josephus was written to be less historical and more descriptive

random_permutation is renamed random since, being a classmethod, it will be addressed as Permutation.random (clearly indicating it is related to Permutation).
3ca01a9
@smichr smichr fix whitespace 285a757
@smichr smichr fix permutations.rst ec6eeac
@smichr smichr only allow af_mul to work from L to R f00fe27
@smichr smichr don't use int_tested in permutations
The significant place not to use it is in __call__ as
noted in the code.

We can let the code fail with a simple int when the user
is working with Cycles.
6bfed40
@smichr smichr fix __eq__/__contains__ for PermutationGroup 0fe75b0
@smichr smichr perm_groups: add has(); cleanup docstrings 0de06e2
@smichr smichr move main make_perm to perm_groups
Standardize the input to the PermutationGroup so a list of permutations
always gets stored in the object (rather than the unacked args):

PG([a, b]) for PG(a, b) and PG([a, b]) rather than PG(a, b) for the
former.

Correct spelling of continuing.
8761a8e
@smichr smichr fix formatting of docstrings 6394e0f
@smichr smichr fix perm_groups docstring 9cfba20
@smichr smichr perm_groups docstring edits f7b73a2
@smichr smichr perm_group: fix corner case error; modify docstrings 823c9f4
@smichr smichr perm_group: fix doctest 0f2e747
@smichr smichr polyhedron: 100% coverage
One of the checks for permutation length was omitted since this
is checked by PermutationGroup.
110a49c
@smichr smichr combinatorics: edits 36fad91
@smichr smichr named_groups: RubikGroup 512768d
@amakelov amakelov Improve multiplication of Permutations with different lengths
Disjoint cycles can be used to instantiate Permutation without
having to include singletons.

Multiplication of permutations of different sizes is handled by
extending the permutation of lesser size so that it matches
the size of the other permutation.
f07cc14
@smichr smichr combinatorics: more classes imported in init 90a40f9
@smichr smichr permutation: call handles list arg; doc edits 3fb9fe5
@smichr smichr permutation: doc edits 8989ade
@smichr smichr permutation: add to docstrings about adjusting mul f538786
@smichr smichr combinatorics: docstrings about Polyhedron and PermutationGroup f7430cb
@smichr smichr polyhedron: let set handle duplicates 0d30bbd
@smichr smichr combinatorics: remove make_perm from polyhedron
There is no need to duplicate the make_perm method in Polyhedron
since Polyhedron stores the permutations in a PermutationGroup
and that group has the make_perm method.
b84e74c
@smichr smichr combinatorics: perm watch for resizing; PermutationGroup adjusts size
Previously, PermutationGroup raised an error if all permutations were
not the same size. Now, in keeping with more flexible disjoint cycle
notation for Permutation, the size of all is simply adjusted so all
have the same maximum value.

More documentation was given to PermutationGroup, too.
35f4fa6
@smichr smichr print PG in cycles; PG accepts cycles; __eq__ ignores size d7aa8db
@smichr smichr combinatorics: global flag for printing in cyclic form
Permutation.print_cyclic will cause Permutations to be printed
as Cycle, e.g. Permutation([0, 2, 1, 3]) -> Cycle(1, 2)

When False, the normal array_form will be used.

This does not affect how cyclic_form and array_form are shown.
So even if the print_cycle flag is True, Permutation([0, 2, 1, 3]).array_form
will still be [0, 2, 1, 3].
ebab47d
@smichr smichr remove __mul__ from Cycle
To avoid confusion, only the __call__ method for Cycle is defined while
__mul__ is defined for Permutation:

Permutation([[1, 2]])*Permutation([[2, 3]]) == Cycle(1, 2)(2, 3)

TODO: a possible confusion is that Permutations printed with print_cyclic
= True print as Cycle(1, 2)(2, 3) (for example) and look like a Cycle
but they are actual a Permutation. So the array_form is defined and the
as_list is not. The Cycle prints as something like [(1, 3, 2)].

>>> a = Permutation([[1,2],[5,3]])
>>> a
Cycle(1, 2)(3, 5)
>>> Cycle(a)
[(1, 2), (3, 5)]
>>> Permutation.print_cyclic = False
>>> a
Permutation([0, 2, 1, 5, 4, 3])
56f483b
@smichr smichr perm_groups: adjust size of input to coset_decomposition 81b9082
@smichr smichr combinatorics: __eq__ uses cyclic_form 041291f
@smichr smichr combinatorics: (a) implement cycle I/O for Permutation c83f573
@smichr smichr permutations: rewrite docs 0c7dd8d
@smichr smichr PERM -> Permutation 9c7d8b5
@smichr smichr coset_rank aligns size of g cbe679d
@smichr smichr permutation: better checking of input c01f1cc
@smichr smichr permutations: fix max() error 0bb3a79
@smichr smichr permutations: add matrix_form method and update docstrings af84c76
@smichr smichr combinatorics: add is_group method and docstring in polyhedron b22723b
@smichr smichr combinatorics: PermutationGroup raises error if gens are duplicated
raise error in PermutationGroup if duplicate permutations are received

remove duplicate permutations from DirectProduct before passing to
PermutationGroup

remove duplicate from names_groups before sending to PermutationGroup

util
    change orbs -> orbits

perm_groups:
    use range instead of xrange in perm_groups
    clean up test for Identity generator
    clean up loop testing whether a new base should be added
    clean up tracking of _base length

permutations:
    make sure perm is a list; if it is a tuple a different obj is
    created -- this is faster than just making it a list and also
    creates a small level of safety for the private routine

    is_Identity will short circuit now without having to generate
    the entire list to make the comparison
9ea9fa7
@smichr smichr combinatorics: simplify is_trivial calc 8a0c0dc
@smichr smichr permutation: work with ints 432ae5d
@smichr smichr permutation: split af_mul into af_muln e0e27df
@smichr smichr permutations: raise error for missing 0; filter gens for PermutationG…
…roup

group_constructs
    use dups=False
named_groups
    use dups=False
perm_groups
    allow duplicate checking to sbe skipped with dups=False keyword
permutations
    don't add 0 by default to permutations entered in array form
    (entering in cycle form automatically adds it, however)

    no: Permutation([2,3,1,4])
    yes:
    >>> Permutation(1, 2, 3, size=5).array_form
    [0, 2, 3, 1, 4]
    >>> Cycle(1,2,3)(4).as_list()
    [0, 2, 3, 1, 4]
    >>> Cycle(1,2,3).as_list(5)

tests modified in keeping with the above
2a08a49
@smichr smichr permutations: differentiate between None and 0 in as_list ebf1cd2
@smichr smichr permutations: add not to lmul docstring about L to R order 30ac89b
@smichr smichr permutations: always show the size when printing 644853b
@smichr smichr perm_groups: clean up coset_decomposition -> coset_factor 445be4a
@smichr smichr coset_factor: return [] instead of False 2cce155
@smichr smichr coset_factor: look for quick exits 873fff4
@smichr smichr perm_groups: coset_repr -> coset_decomposition 48b953a
@smichr smichr permutations: return transpositions factors as list, not set f01d0d6
@smichr smichr _af_mul special cases up to 8 factors d976353
@smichr smichr combinatorics: lmul -> mul; __xor__ -> conjugate
The computation of the product of more than 8 items in _af_mul (now
_af_rmul) was made to only create 1 intermediate list.

The discussion about R or L multiplication was dropped in favor of
just demonstrating what was meant, e.g. a(b(c(i))), and giving a
corresponding doctest.

The __xor__ method was mapped to conjugate so now a^b given a.conjugate(b)

The subgroup_search test for size 16 was xfailed as slow.
2016b3d
@smichr smichr combinatorics: Permutations not equal unless size is equal, too
Things are slowed down considerably when testing cyclic forms, and
allowing permutations of different lengths to compare equal leaves
open the possibiliity of subtle errors. Permutations now compare
equal only if their array forms are equal.

An error is also raise for coset_factor if the g to be factored is
not the same length as the permutations in the group.
c6cf17e
@smichr smichr permutations: fix sphinx error 2f1ab6e
@smichr smichr polyhedron: reorder tetrahedron permutations 0e03412
@smichr smichr cube: add inline references for the pgroup 188f178
@smichr smichr permutations: make sure largest is printed first in permutation cycli…
…c form

The output should be copyable and pastable for reevaluation, and it
won't be if it is printed like Permutation(1,2)(5) instead of
Permutation(5)(1,2).
9d2067a
@smichr smichr perm_groups: minor docstring edits 7c292d8
@smichr smichr permutations: cleanup docstring 0424fdf
@smichr smichr permutation: define _hashable_content 4595ff4
@smichr smichr permutations: remove cauchy form; edit docstrings cb18cb5
@rlamy rlamy Move int_tested to the core and refactor it.
* Move int_tested() to sympy.core.compatibility, to make it
  importable anywhere.
* Simplify it to take only a single argument, like int().
* Rename it as_int().

cps: add compatibility to core.rst
717d4e3
@smichr smichr permutations: restore recursive _af_rmul 44da66e
@smichr smichr polyhedron: edit docstrings 98f2ed9
@smichr smichr permutations: change docstring so r*q!=q*r 277274a
@smichr smichr prem_groups: coset_decomposition -> stabilizer_cosets; stabilizers_ge…
…ns -> stabilizer_gens
68a8d33
@smichr smichr permutations: add note about static arrow diagrams 0750839
@smichr smichr permutations: add as_permutation method
This is inspired by the Mathematica function FindPermutation.

dec
208c021
@smichr smichr perm_groups: expand is_group docstring and modify generate docstring d6e08ca
@smichr smichr permutations: remove __xor__ operator
This is being removed until the question of whether a.conjugate(b)
should give ~a*b*a or a*b*~a.
09aae58
@smichr smichr permutations: change commutator and conjugate docstrings
Permutations were selected for commutator so xp~x~p != ~p~xpx
2c68a68
@smichr smichr as_permutation -> from_sequence ea6cbeb
@pernici pernici permutations: used __xor__ instead pf __pow__ for conjugation
In addition,

used __rxor__ to represent the application i --> i^p

Changes by Chris Smith (smichr@gmail.com)

removed conjugate method in favor or only the __xor__ method
and introduced a test function, conjugate_of to test whether
self and other are conjugates of each other.

other docstring edits
f74a09e
@pernici
    def conjugate_of(self, other):
        """Return True if self and other are conjugates, else False.

If two permutations have the same cycle structure, they are conjugates
in S_n, but not necessarily in another permutation group.

see http://math.stackexchange.com/questions/48134/why-are-two-permutations-conjugate-iff-they-have-the-same-cycle-structure

I suggest to specify that it is a test whether they are conjugate in S_n; the alternative is to add as default
parameter group=S_n, compute h such that self = other^h and check if h belongs to the group,
but I do not think it is a good idea.

@pernici

I do not think it is a good idea to introduce the division by a permutation, since permutations do not commute.

@asmeurer
Owner

I haven't been following this or the other PR that it shadows very closely. Please ping me when it's ready to be merged.

smichr and others added some commits
@smichr smichr permutations: +/- on basis of rank
Partitions and Prufer add on the basis of rank and this seems to
be a better idea than adding the elements of the inversion code
together.

rank(i) now gives ith permutation

modify which form of the commutator is returned
523f73a
@smichr smichr named_groups: add the 3 polyhedral groups 6903994
@smichr smichr permutations: Cycle gets size property and __iter__ 4487a92
@smichr smichr permutations: as_list -> list method fa90805
@smichr smichr permutations: remove conjugate_of 0a31443
@smichr smichr named_groups: remove Polyhedral groups
I'm not confident enough that I understand what someone would expect
for these groups, so I'm leaving them out. They should be easy to add
back and modify if necessary.
61a9211
@smichr smichr permutations: remove test functions 660108c
@smichr smichr permutations: cycle_structure method added e952282
@smichr smichr permutations: add note about getting h*f*~h instead of ~h*f*h 029ca86
@smichr smichr iterables: lazyDSU_sort added ae76b99
@smichr smichr permutations: make cycle_structure a property; return dict 72d63d7
@smichr smichr perm_groups: remove has; add _hashable_content 165707f
@smichr smichr fix permutation str test 569d161
@smichr smichr permutation: Permutation has list method, too 0cc5f75
@smichr smichr move quick_sort to compatibility 3928e43
@smichr smichr permutations: add to contains docstring 2fb0465
@smichr smichr fix sphinx errors in compatibility 68217a3
@smichr smichr fix Permutation and PermutationGroup args 69f14da
@smichr smichr perm_groups: minor edits of insert
There is no need to append element [0,...,cmin] to the end of the
range starting from cmin since negative indices handle this just
fine.

ig was changed to gi since it represents g[i] and ig is similar to jg
which is short for JerrumGraph.

There is no need to do double work in testing to see if g is Identity
and then search for the min change. If there is no min change then
it's the identity. The only caveat would be if one got a g like
[2,1,0,3,4,5] and alpha was 3: None would be returned and the routine
would have failed. For safety, an assertion could be made that if i
is None then g[:cmin] == range(cmin)...this has not been done and no
test has failed to it was left out.
5faabf9
@smichr smichr named_groups: fix docstring d3989e4
@smichr smichr examples: minor edits d6d8759
@smichr smichr add reshape to iterables 1692d79
@smichr smichr perm_groups: remove bisect 912ff2e
@smichr smichr fix str error for PermutationGroup dee3ba2
@pernici pernici Changed is_subgroup to admit that a group can be subgroup of a group
with larger degree
69e60a3
@smichr smichr fix str error f36b1de
@smichr smichr Merge pull request #15 from pernici/combinatorics
Changed is_subgroup to admit that a group can be subgroup of a group with larger degree
cd0ff48
@smichr smichr Merge branch 'combinatorics' of github.com:smichr/sympy into combinat…
…orics
5da3f9f
@smichr smichr fix perm_group docstring 2a86561
@smichr smichr perm: add trivial check to contains 1f6dcca
@smichr smichr permutations: removed unused imports and tuplizatation of args 1ea38fa
@smichr smichr fix trouble with hash and PermutationGroup
The redefining of __eq__ required that __hash__ also be defined
as noted now in the __eq__ docstring that was edited in Basic.
32a7c3a
@smichr smichr perm_groups: minor edits 5d82051
@smichr smichr move __eq__ to contains; __eq__ is more literal 556cc4e
@smichr smichr move the old __eq__ code from contains to is_in e1ef520
@smichr smichr merge is_in and is_subgroup with strict option 9668728
@pernici pernici fixed bug in the computation of the strong base 6a4830a
@pernici pernici Added default argument `strict` to is_transitive` 8df042c
@smichr smichr Merge pull request #16 from pernici/combinatorics
Added default argument `strict` to is_transitive`; fixed bug in `base`
eeb5b73
@smichr smichr is_transitive: False for strict=False possible 77fe489
@smichr smichr contains gets strict option e1b091b
@smichr smichr resize in contains rather than in is_subgroup ea5bf22
@pernici pernici Faster coset_factor and coset_rank 51a50f1
@smichr smichr stabilizer_cosets, coset_factor and gens come back as permutations
af=True will send them back in array form
d08c9dc
@smichr smichr fix error in simplify with use of has_variety 419d310
@smichr
Collaborator

#1508 is now in.

@smichr smichr closed this
@coveralls

Coverage Status

Changes Unknown when pulling 419d310 on smichr:combinatorics into ** on sympy:master**.

@coveralls

Coverage Status

Changes Unknown when pulling 419d310 on smichr:combinatorics into ** on sympy:master**.

@coveralls

Coverage Status

Changes Unknown when pulling 419d310 on smichr:combinatorics into ** on sympy:master**.

@coveralls

Coverage Status

Changes Unknown when pulling 419d310 on smichr:combinatorics into ** on sympy:master**.

@coveralls

Coverage Status

Changes Unknown when pulling 419d310 on smichr:combinatorics into ** on sympy:master**.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Something went wrong with that request. Please try again.