I think this is a weak point for J's array programming style. Just like tacit code works well for small manipulations but gets confusing and difficult as the number of arguments increases, J primitives like rank, transpose, and inner product are excellent for a few axes, but don't work so well when the number of axes increases and the ways they are manipulated become more complicated. In J's defense, most programming languages are pretty awful at this (how many times are you going to write .map before you just give up?). But it's something that bothers me. Sure, I can write a bunch of nested rank operators to do what I want, but why is it so hard to just say which axes correspond to which?
This is a known "shortcoming". Arthur Whitney described his thinking on this as follows: The programmer _should_ design his/her data so that compatible axes are at the trailing end, because current (and foreseeable) computing architecture will be much faster if the data to be worked on are adjacent in memory. See also Bob Bernecky's APL88 paper [Bernecky 1987] for a description of the shortcomings of the axis operator (and likely of any extensions of the axis operator). Bernecky, R., An Introduction to Function Rank, APL88 Conference Proceedings, APL Quote Quad, Volume 18, Number 2, 1987-12. (There is a copy of the APL88 Conf. Proc. on a shelf in Bramley.) On Thu, Feb 6, 2020 at 2:31 PM Marshall Lochbaum <[email protected]> wrote: > Here is a way to permute the axes of an array that you might find easier > to work with: > > A =. i. 2 3 4 5 > $ ('ijkl'i.'ljik') |: A > 5 3 2 4 > > The idea is that we start with A, which has axes ijkl, and want to > shuffle them around to get ljik. So we look up each of the letters ljik > in ijkl, which gives us a permutation 3 1 0 2 which should be applied to > the axes (you could also just write out the permutation by hand). This > is then used as the left argument to transpose. > > Although a matrix product will execute faster, if I want to write tensor > code that is straightforward, and extends to multiple contracted axes, I > would transpose the contracted axes to the end and combine them with > (+/@:*"1). This pairs row vectors from the two arrays and takes their > dot product. For three contracted axes I might write (+/@:,@:*"3), which > differs in that it pairs up rank-3 subarrays, and ravels before summing, > since sum only operates on the first axis. > > The idea with (|:~ 1 -.~ i.@(#@$)) is to get the list of indices for > every axis > > i.@(#@$) A > 0 1 2 3 > > and then remove a 1 > > 1 -.~ i.@(#@$) A > 0 2 3 > > to obtain a left argument to transpose. Transpose adds missing axes to > the front, so this will move axis 1 to the beginning, for an array of > any shape. > > I think this is a weak point for J's array programming style. Just like > tacit code works well for small manipulations but gets confusing and > difficult as the number of arguments increases, J primitives like rank, > transpose, and inner product are excellent for a few axes, but don't > work so well when the number of axes increases and the ways they are > manipulated become more complicated. In J's defense, most programming > languages are pretty awful at this (how many times are you going to > write .map before you just give up?). But it's something that bothers > me. Sure, I can write a bunch of nested rank operators to do what I > want, but why is it so hard to just say which axes correspond to which? > > Marshall > > On Thu, Feb 06, 2020 at 09:44:32PM +0000, [email protected] wrote: > > Henry Rich <[email protected]> writes: > > > > > It depends on what the '*' means in the definition of the product. > > > > >> C_ijlmnop = sum_k A_ijkl * B_mknop > > > > I really mean the usual multiplication (of numbers). > > > > > > > Have a look at > > > > > > a =. i. 2 3 4 5 > > > b =. i. 2 4 3 5 6 > > > $ (2 |: a) +/ . * ((|:~ 1 -.~ i.@(#@$)) b) > > > 2 3 5 2 3 5 6 > > > > Well, I hoped for seeing something simpler. :) > > > > If I understand your example a bit, you sum along the 4-long axis, and > > you make this axis the last in the case of 'a', ie. by (2 |: a), then > > use the so called dot product (+/ . *), and then something I cannot > > easily decipher, but I guess it must, among other things, make the > > 4-long axis (number 1) the first. > > > > Does one really has to shuffle with the axes? > > Does one really need to use the for me almost incomprehensible > > conjunction . (which no one dares to explain simply enough...)? > > > > Why is it so simple to say what I want mathematically, and so awkward > > to do it programmatically? > > > > Thanks! > > Ruda > > > > > > > On 2/6/2020 2:46 PM, [email protected] wrote: > > >> having two multidimensional matrices A and B, > > >> with some indices, say, A_ijkl and B_mknop, > > >> how can I obtain a matrix C, where > > >> > > >> C_ijlmnop = sum_k A_ijkl * B_mknop > > >> > > >> ie., C has all indices of A and B but for the index k, > > >> which was summed over. > > >> > > >> Thanks for your suggestions. > > >> > > >> Best regards > > >> Ruda > > >> ---------------------------------------------------------------------- > > >> For information about J forums see > http://www.jsoftware.com/forums.htm > > > > > > ---------------------------------------------------------------------- > > > For information about J forums see http://www.jsoftware.com/forums.htm > > ---------------------------------------------------------------------- > > For information about J forums see http://www.jsoftware.com/forums.htm > ---------------------------------------------------------------------- > For information about J forums see http://www.jsoftware.com/forums.htm > ---------------------------------------------------------------------- For information about J forums see http://www.jsoftware.com/forums.htm
