Re: The Case of the Mystery Option
- To: mathgroup at christensen.cybernetics.net
- Subject: [mg1062] Re: The Case of the Mystery Option
- From: villegas (Robert Villegas)
- Date: Fri, 12 May 1995 15:40:51 -0400
- Organization: Wolfram Research, Inc.
Jack Goldberg <jackgold at math.lsa.umich.edu> writes: > (1) Is it true that the 2nd line on page 132 of The Book is a > misprint? It says that Transpose[list,n] interchanges the top > level with the nth level. Either a misprint or a bug. Here's a workaround: In[5]:= Unprotect[Transpose] Out[5]= {Transpose} In[6]:= Transpose[tensor_, n_Integer?Positive] := Transpose[tensor, Range @ TensorRank[tensor] /. {1 -> n, n -> 1}] Here's a test, on a matrix whose entries just display their indices in the matrix (the 2,5,3 element displays as "253" for convenience of viewing): In[7]:= Transpose[Array[SequenceForm, {2, 3, 4}], 3] Out[7]= {{{111, 211}, {121, 221}, {131, 231}}, > {{112, 212}, {122, 222}, {132, 232}}, > {{113, 213}, {123, 223}, {133, 233}}, > {{114, 214}, {124, 224}, {134, 234}}} > (2) From the error messages I have received while experimenting > with this option, I have deduced that the option is a permutation. > It permutes levels? Is this correct? The form you are talking about is U = Transpose[T, {p1, p2, ..., pn}] where T is an n-dimensional tensor (i.e. TensorRank[T] == n). It is true that {p1, p2, ..., pn} is a permutation of the levels of T. This permutation says that level 1 in T becomes level p1 in U; level 2 in T becomes level p2 in U; . . . ; and level n in T becomes level pn in U. As a mapping, you could write this as {1 -> p1, 2 -> p2, . . . , n -> pn} That's fairly simple to state, but awfully abstract, and I had to look at a bunch of examples to make sense of it, so let's look at one. Let's say the permutation is {3, 1, 2}. So the change of levels, or the change of indices, is {1 -> 3, 2 -> 1, 3 -> 2} What this means is: if you pick an object in T whose 1st index is 5, then that same object exists in U, but with its 3rd index being 5. And if that object had a 2nd index of 8 in T, then its position in U will have a 1st index of 8. And if its 3rd index was 7 in T, then its 2nd index will be 7 in U. So U(8, 7, 5) = T(5, 8, 7) It's the same object (say a real number), but it has moved from the cell with coordinates of {5, 8, 7} to the cell with coordinates of {8, 7, 5}. If you have the coordinates of an entry of U, say newcoords, then you can find the corresponding coordinates in T as follows: oldcoords = Part[newcoords, {p1, p2, ..., pn}] We'll define a tensor in Mathematica and then Transpose it to see how this rule works. In[16]:= t = Array[SequenceForm, {2, 3, 4}] Out[16]= {{{111, 112, 113, 114}, {121, 122, 123, 124}, {131, 132, 133, 134}}, > {{211, 212, 213, 214}, {221, 222, 223, 224}, {231, 232, 233, 234}}} In[17]:= u = Transpose[t, {3, 1, 2}] Out[17]= {{{111, 211}, {112, 212}, {113, 213}, {114, 214}}, > {{121, 221}, {122, 222}, {123, 223}, {124, 224}}, > {{131, 231}, {132, 232}, {133, 233}, {134, 234}}} So the change of levels, i.e the change of indices, from t to u is 1 -> 3, 2 -> 1, 3 -> 2. Suppose we want to find out where the element at {2, 2, 3} in t went. Well, moving 1st to 3rd, 2nd to 1st, and 3rd to 2nd, we get new indices of {2, 3, 2}. We can check that this position in u contains the same object that {2, 2, 3} did in t using Part. All the entries that I generated with Array are distinct, so there is no possibility of different entries being equal. In[20]:= Part[u, 2, 3, 2] === Part[t, 2, 2, 3] Out[20]= True > (3) How does one explain Maeder's example? Maeder says that when the p's in your level re-arrangement {p1, p2, ..., pn} are not distinct, then Transpose takes the diagonal. Remember that the p's give you the destination levels of the original levels. If I have a 3-tensor and I specify {1, 1, 1}, then I'm saying that all levels end up at the first level of the new tensor. The new one will have just one level then, all three of the original ones having collapsed to it. And all indices will vary simultaneously since they have gone to the same level. That's another way of saying "diagonal". By the way, this requires that all three of the original dimensions had to have the same length k, because they're all going to vary from 1 to k together. Here's a 3 x 3 x 3 that collapses to a 3-element vector under the {1, 1, 1} transpose operation: In[22]:= m3 = Array[SequenceForm, {3, 3, 3}] Out[22]= {{{111, 112, 113}, {121, 122, 123}, {131, 132, 133}}, > {{211, 212, 213}, {221, 222, 223}, {231, 232, 233}}, > {{311, 312, 313}, {321, 322, 323}, {331, 332, 333}}} In[23]:= Transpose[m3, {1, 1, 1}] Out[23]= {111, 222, 333} I don't have to collapse all the original levels to the same new level. For instance, I could take the 1st and 3rd indices to level 1. In[24]:= Transpose[m3, {1, 2, 1}] Out[24]= {{111, 121, 131}, {212, 222, 232}, {313, 323, 333}} Notice that the 1st and 3rd indices are always equal, and they vary as I move across the elements of level 1 of Out[24] (they don't change within any particular element). The 2nd index changes as you move across the elements of any sublist (level 2). Since giving non-distinct destinations means two or more dimensions will merge into one, the result won't have as many dimensions as the original. Hence, I can't specify a destination level of 3 for the original 2nd index, because the result is not going to have 3 levels. In[25]:= Transpose[m3, {1, 3, 1}] Transpose::perm: {1, 3, 1} is not a permutation. Out[25]= Transpose[{{{111, 112, 113}, {121, 122, 123}, {131, 132, 133}}, > {{211, 212, 213}, {221, 222, 223}, {231, 232, 233}}, > {{311, 312, 313}, {321, 322, 323}, {331, 332, 333}}}, {1, 3, 1}] If I give a "permutation" {p1, p2, ..., pk} with r distinct integers in it, then they must all be <= r. Here's an example of collapsing a 5-tensor to a 2-tensor by taking the diagonal in dimensions 1, 3, and 5, and another diagonal in dimensions 2 and 4: In[37]:= Transpose[Array[SequenceForm, {3, 3, 3, 3, 3}], {1, 2, 1, 2, 1}] Out[37]= {{11111, 12121, 13131}, {21212, 22222, 23232}, {31313, 32323, 33333}} Indices 1, 3, and 5 change as you move across level 1, whereas indices 2 and 4 change as you move across level 2 (the elements of the sublists). To recap two caveats regarding non-distinct pi: (1) If you map two levels to the same level, they must have the same length, otherwise you can't take their diagonal (well, I think there are pseudo diagonals or something for rectangular matrices, but the code doesn't allow that). (2) If you have only r distinct destination levels, then all destination levels must be <= r. Moving levels around is a little difficult to visualize & conceptualize. Explaining it seems even harder, and I'm sure there are other ways to look at this that might be even better than my attempt. I'm trying to come up with something geometric, although I'm not sure how it'll survive the ASCII art projection... Robby Villegas