# Problem with Custom Sort/Split/Gather

(extreme TL;DR at bottom)

I have a set of symbols (which represent 2-vectors in a plane, ultimately) and I want to sort/split/group them symbolically without yet inserting numerical values.

## Numeric (fully functional)

I have produced fully functional code with numerical values.

qq[1] = {0, 1}; qq[2] = {-Sqrt[3], -1}/2; qq[3] = {Sqrt[3], -1}/2;φ[{kx_, ky_}] = ArcTan[kx, ky];
Block[{order = 15, rings, stars}, rings[0] = {{0, 0}}; stars[0, 0] = rings[0]; rings[1] = Table[qq[s], {s, 1, 3}]; Do[rings[r] = Complement[    Flatten[Table[rings[r - 1][[s]] + (-1)^(r - 1) qq[σ], {s, 1,        Length[rings[r - 1]]}, {σ, 1, 3}], 1], rings[r - 2]], {r, 2, order}]; Do[stars[r, s] = Sort[GatherBy[rings[r], Norm][[s]], φ[#1] < φ[#2] &], {r, 1, order}, {s, 1, Length@GatherBy[rings[r], Norm]}];Column[{stars[1, 1], stars[3, 1], stars[3, 2], Row[{stars[3, 7], "  <-intentionally not defined, for example"}]}]]


Explanation:
The code initializes rings[0], rings[1], and stars[0,0].
Then it produces many rings[r], special sets of vectors which are produces by linear combinations of the qq[j]. This is done iteratively by adding certain qq[j] to the previous rings[r], and the Complement ensures that both duplicates are discarded as well as removing elements which exist in rings[r-2] already.
Then special sets stars[r,s] are produced. First, GatherBy is used to collect the elements in terms of their magnitude (Norm). Then, the elements are Sorted by a pure function which uses the \phi (ArcTan) function defined.
The Column portion of the code is included only to sample the desired output.

The elements, special linear combinations of the qq[j], are the "symbols" that need to be both Gathered and Sorted.

## Symbolic (not yet functioning properly)

My best attempt to make the above code work without yet specifying the values of the qq[j] is:

Lφ[{a_, b_, c_}] = ArcTan[Sqrt[3] (c - b), 2 a - b - c];Lmag[{a_, b_, c_}] = Sqrt[Abs[a - b/2 - c/2]^2 + (3/4) Abs[b - c]^2]; Block[{order = 15,  rings, stars, q1, q2, q3}, rings[0] = {0}; stars[0, 0] = rings[0]; rings[1] = {q1, q2, q3}; Do[rings[r] = Complement[Flatten[Table[rings[r - 1][[s]]+(-1)^(r - 1) Boole[σ ==1] q1 + (-1)^(r - 1) Boole[σ ==2] q2 + (-1)^(r-1) Boole[σ == 3] q3, {s, 1,
Length[rings[r - 1]]}, {σ, 1, 3}], 1],
rings[r - 2]], {r, 2, order}]; Do[stars[r, s] = Sort[Gather[rings[r],
Lmag@Coefficient[rings[r][[#1]], {q1, q2, q3}] ==
Lmag@Coefficient[rings[r][[#2]], {q1, q2, q3}] &][[s]],
Lφ@Coefficient[rings[r][[#1]], {q1, q2, q3}] <=
Lφ@Coefficient[rings[r][[#2]], {q1, q2, q3}] &], {r,   1, order}, {s, 1, Length@GatherBy[rings[r], Norm]}];]


This code gives several errors which are really not the problem with which I would primarily request your assistance, however. Note that I have checked, and the rings[r] part of the symbolic code is fully functional. Only the stars[r,s] portion does not work, because of the symbolic gathering and sorting.

To get at the root of my problem:

## MWE

This code illustrates that my functions are capable of finding the magnitudes of the elements, and of course those numerical values can be sorted or gathered or split.

Lφ[{a_, b_, c_}] = ArcTan[Sqrt[3] (c - b), 2 a - b - c];Lmag[{a_, b_, c_}] = Sqrt[Abs[a - b/2 - c/2]^2 + (3/4) Abs[b - c]^2]; Block[{order = 15,  rings, stars, q1, q2, q3}, rings[0] = {0}; stars[0, 0] = rings[0]; rings[1] = {q1, q2, q3}; Do[rings[r] =Complement[Flatten[Table[rings[r - 1][[s]] + (-1)^(r-1) Boole[σ ==1] q1 + (-1)^(r - 1) Boole[σ ==2] q2 +(-1)^(r - 1) Boole[σ == 3] q3, {s, 1,Length[rings[r - 1]]}, {σ,1,3}], 1], rings[r - 2]], {r, 2, order}];Column[{Table[   Lmag@Coefficient[rings[3(*r*)][[s]], {q1, q2, q3}], {s, 1, 9}],Sort@Table[
Lmag@Coefficient[rings[3(*r*)][[s]], {q1, q2, q3}], {s, 1, 9}],   Gather@Sort@Table[Lmag@Coefficient[rings[3(*r*)][[s]], {q1, q2, q3}], {s, 1,
9}], Split@
Sort@Table[
Lmag@Coefficient[rings[3(*r*)][[s]], {q1, q2, q3}], {s, 1,
9}]}]]


However, if I replace the body of this MWE (starting with Column) with something like

Gather[rings[3], Lmag@Coefficient[rings[3][[#1]], {q1, q2, q3}] &];


or Sort, SortBy, Split, SplitBy, GatherBy, GroupBy, etc., with similar 1 argument or two-argument comparison pure functions, I either get errors or outputs that are not properly sorted or grouped, etc.

Once I can properly gather the symbolic expressions (for example, in this given example, q1-q2+q3 should be in stars[3,1] but 2q1-q2 should be in stars[3,2]), then sorting them by a pure function in the phase may be similar, but assisting me with that as well would be greatly appreciated!

## Extreme Context-Free TL;DR Version

Why doesn't this Sort properly?

Lmag[{a_, b_, c_}] = Sqrt[Abs[a - b/2 - c/2]^2 + (3/4) Abs[b - c]^2];
Block[{rings, q1, q2, q3},
rings[3] = {2 q1 - q2,  2 q2 - q1,
2 q1 - q3,    q1 + q2 - q3,
2 q2 - q3,    q1 - q2 + q3,
-q1 + q2 + q3,    2 q3 - q1,
2 q3 - q2};
Print@Table[Lmag@Coefficient[rings[3][[s]], {q1, q2, q3}], {s, 1, 9}];
Print@rings[3];
Sort[rings[3],
Lmag@Coefficient[rings[3][[#1]], {q1, q2, q3}] <=Lmag@Coefficient[rings[3][[#2]],
{q1, q2, q3}] &]]

• without unraveling the whole thing i see a couple of uses of things like Column and Row. Those should be used strictly for output formatting, if you are trying to sort things after applying such format functions that could be the source of your trouble. BTW You should throw in some returns so your code can be read without too much side to side scrolling.. – george2079 Feb 23 '16 at 15:57
• The Column and Rows are only for output, just so people trying to help can see what it does and where it goes right or wrong, I'm not trying to sort/split/gather after those. As for the code, I made it this way so that you can copy and paste the entire block into mma and run it, but indeed, it is not really readable here. Did you try the Extreme TL;DR version? There's really no unraveling there, just a Sort with a pure function that fails. – Steve Feb 23 '16 at 16:01
• I think your very last sort should be like this: Sort[rings[3], Lmag@Coefficient[#1, {q1, q2, q3}] <= Lmag@Coefficient[#2, {q1, q2, q3}] &]. The items passed to the sort function are the actual list items, not indices. – george2079 Feb 23 '16 at 16:10
• Wow, thank you! This actually does work. Obviously my grasp of pure functions is weak. So with "Sort[X,f(#) &]", # takes the place of the entire X? But with "Sort[X,f(#1) &]" then #1 automatically treats X as a list and takes the [[1]] part? In other words, you really would never use #1 and #2 unless there is specifically a list? – Steve Feb 23 '16 at 16:34
• Sort always passes pairs of list items. # is the same as #1. Running something like this should be instructive: Sort[Range[4], (Print["args", {#1, #2}]; #1 > #2) &] – george2079 Feb 23 '16 at 18:17

With a lot of help from george2079 and Jack LaVigne, I have arrived at a solution to my issue.

1. Part of my problem was the syntax for my pure functions
2. Additionally, GatherBy does not order the subgroups in a "sorted" way.
3. I couldn't "distribute" a pure function Sort over a list of lists.

Given these considerations, something like

GatherBy[Sort[rings[3],Lmag@Coefficient[#1, {q1, q2, q3}] < Lmag@Coefficient[#2, {q1, q2, q3}] &], Lmag@Coefficient[#, {q1, q2, q3}] &];


Will give the proper subsets ordered by their Norms. First, you must Sort the list rings[3] by magnitude, and then GatherBy to separate the subsets. For some reason, if you don't sort them first, GatherBy will arrange the subsets in order of the first appearance of each equivalence class! This behavior is unusual, because GatherBy does not work this way for the Numerical case!

Now, I don't have mma v10 which includes a similar command GroupBy (I actually can't tell how it differs from GatherBy) so there is a chance, I suppose, that function might accomplish both goals simultaneously.

In the numeric case, I could then simply "distribute" a Sort over these lists of lists to sort by the ArcTan function. Perhaps if I were more adept at pure functions, this could still be done in a single step, I'm not sure. Nonetheless, it can be done by simply sorting each sublist individually, which is fine for my purpose.

With that, should anyone come along and care to see it, here is a fully functional solution to my original post

Block[{order = 4, rings, splitrings, stars, q1, q2, q3}, rings[0] = {0}; stars[0, 0] = rings[0]; rings[1] = {q1, q2, q3}; Do[rings[r] = Complement[
Flatten[Table[rings[r - 1][[s]] + (-1)^(r - 1) Boole[σ ==1] q1 +(-1)^(r - 1) Boole[σ ==2] q2 + (-1)^(r - 1) Boole[σ == 3] q3, {s, 1,Length[rings[r - 1]]}, {σ, 1, 3}], 1],
rings[r - 2]], {r, 2, order}]; Do[splitrings[r] = GatherBy[   Sort[rings[r], Lmag@Coefficient[#1, {q1, q2, q3}] <
Lmag@Coefficient[#2, {q1, q2, q3}] &],
Lmag@Coefficient[#, {q1, q2, q3}] &], {r, 1, order}]; Do[stars[r, s] =    Sort[splitrings[r][[s]], Lφ@Coefficient[#1, {q1, q2, q3}] <
Lφ@Coefficient[#2, {q1, q2, q3}] &], {r, 1, order}, {s, 1, Length@splitrings[r]}]]

• RE: "GatherBy does not order the subgroups in a sorted way" Exactly. The pure function you supply to GatherBy only returns True or False. What criteria would you expect it to use for sorting? – george2079 Feb 24 '16 at 16:33
• While that makes sense, I used the exact same system with my "numeric" case and GatherBy did automatically sort them in increasing order. That's the difference that led to the confusion, I guess. – Steve Feb 24 '16 at 17:05

However, if I replace the body of this MWE (starting with Column) with something like

Gather[rings[3], Lmag@Coefficient[rings[3][[#1]], {q1, q2, q3}] &];


I see a problem with the syntax. I replaced it with

Gather[rings[3], Lmag@Coefficient[#1, {q1, q2, q3}] < Lmag@Coefficient[#2, {q1, q2, q3}] &]


and got an answer.

Block[
{order = 15, rings, stars, q1, q2, q3},
rings[0] = {0};
stars[0, 0] = rings[0];
rings[1] = {q1, q2, q3};

Do[rings[r] = Complement[
Flatten[
Table[
rings[r - 1][[s]] + (-1)^(r - 1) Boole[σ ==
1] q1 + (-1)^(r - 1) Boole[σ ==
2] q2 + (-1)^(r - 1) Boole[σ == 3] q3,
{s, 1, Length[rings[r - 1]]}, {σ, 1, 3}],
1],
rings[r - 2]
],
{r, 2, order}
];

Gather[rings[3],
Lmag@Coefficient[#1, {q1, q2, q3}] <
Lmag@Coefficient[#2, {q1, q2, q3}] &]
]


with no errors

{{2 q1 - q2}, {-q1 + 2 q2}, {2 q1 - q3}, {q1 + q2 - q3,
2 q2 - q3, -q1 + 2 q3, -q2 + 2 q3}, {q1 - q2 + q3}, {-q1 + q2 + q3}}


I don't know enough about the problem to know whether the answer is correct but hopefully this should get your pointed in the right direction.

• Thank you for the syntax error! Indeed, someone else just found that in the comments a moment ago. I understand that a reader here wouldn't know, but, unfortunately that output is not the desired. The desired output would be a list of two lists. The first list should contain 3 elements which are linear combinations including all 3 qs (like q1-q2+q3). The second list should contain 6 elements which are linear combinations including a prefactor 2 (like -q1+2 q2). – Steve Feb 23 '16 at 16:36
• Gather[rings[3], Lmag@Coefficient[#1, {q1, q2, q3}] <= Lmag@Coefficient[#2, {q1, q2, q3}] &] gives almost the correct result, except that the two sets are backwards in that case. You can check things like N@Lmag[{2, -1, 0}] and N@Lmag[{1, 1, -1}] to see that the Gather is collecting the larger "Sqrt[7]-magnitude" elements before the smaller "2-magnitude" elements – Steve Feb 23 '16 at 16:50
• Similarly GatherBy[rings[3], Lmag@Coefficient[#1, {q1, q2, q3}] &] gives the output in reversed order? Obviously I could Reverse it, but I need to understand it much more generally for later cases with many more subsets. – Steve Feb 23 '16 at 16:51