On Thu, Sep 16, 2010 at 10:51 AM, Justin Paston-Cooper
<[email protected]> wrote:
> Now comes the task of trying to understand the code.
When I am trying to read unfamiliar J code, one thing I always
like to do is look at sample results for sample data.
Once I understand how the data is being transformed, I can better
focus on how that is happening.
I am going to try to hit an intermediate level of description
here -- I am probably going to go too fast in some places
and too slow in others. But if something does not make
sense feel free to experiment and/or pose questions:
A =: 2-4|6<.|(-|:)i.4 4
A byLevel 1 1 1 1
A was just a slightly compressed version of your
original A. It accomplishes that your original literal
did not. But if you are interested in what is happening,
you can look at the intermediate results.
In other words:
i.4 4
(-|:)i.4 4
6<.(-|:)i.4 4
4|6<.(-|:)i.4 4
2-4|6<.(-|:)i.4 4
Anyways, for the code itself...
The first significant line is:
mask=. </~i.{:$y
And with y = 1 1 1 1 this gives us:
0 1 1 1
0 0 1 1
0 0 0 1
0 0 0 0
{:$y is the number of columns in y (and if y is always a vector you could
use #y instead of {:$y). For this example the result is 4.
i.4 is 0 1 2 3
and </~0 1 2 3 is a 4 by 4 boolean upper triangular matrix. (It's a "less
than" table, much like +/~ would be an addition table and */~ would be
a times table.)
reflect is a function definition, so is not very interesting by
itself. The first time it runs, it does:
(0&< * ] -"1"1 2 A *"2 1 ]) ,:1 1 1 1
which is equivalent to
(,:1 1 1 1) * (,:1 1 1 1) -"1"1 2 A *"2 1 ,:1 1 1 1
Looking at the intermediate results, we get:
A *"2 1 ,:1 1 1 1
2 _1 0 0
_1 2 _1 0
0 _1 2 _1
0 0 _1 2
That looks like almost nothing happened to A, but:
$A *"2 1 ,:1 1 1 1
1 4 4
...the result is structured with one item for each list of weights.
And carrying the calculation a bit further, we get:
(,:1 1 1 1) -"1"1 2 A *"2 1 ,:1 1 1 1
_1 2 1 1
2 _1 2 1
1 2 _1 2
1 1 2 _1
Our next batch of weights (all of which are viable).
reflect=: 0&< * ] -"1"1 2 A *"2 1 ]
reflect reflect ,:1 1 1 1
0 0 0 0
1 _2 3 1
_1 3 _1 2
_1 2 2 _1
_2 1 2 1
0 0 0 0
2 1 _2 3
2 _1 3 _1
_1 3 _1 2
3 _2 1 2
0 0 0 0
1 2 1 _2
_1 2 2 _1
2 _1 3 _1
1 3 _2 1
0 0 0 0
We see that we get a lot of weights out of this calculation. The rows of
zeros correspond items that would have been eliminated in your
'newFromWeight' -- where you were generating a set of indices where the
right argument was greater than zero. Here, instead of eliminating
the calculation from the result (which results in an irregular result), I
am forcing the result of the calculation to be zero. The part of reflect
which accomplishes this is:
0&< * ...
(where ... is the part of reflect that calculates the previous intermediate
result).
This works because the dimensions of your weights correspond to
the first two dimensions this result which I am generating.
But lets go over the steps here in reflect:
A *"2 1 reflect ,:1 1 1 1
This multiplies A by each row from your previous weights.
That means that each row in A gets multiplied by each scalar
from the previous set of weights.
(reflect,:1 1 1 1) -"1"1 2 x *"2 1 reflect,:1 1 1 1
The subtraction here is similar -- the product of A and
the weights is subtracted from the corresponding row
of weights. But we force that subtraction to be row-at-a-time.
Anyways, you had an additional requirement for an acceptable set of weights,
and not all of the non-zero rows are valid. This logic is encoded in 'keep'.
mask=:</~i.4
keep=: (0 < ^&mask"2) >&(*./"1) =&0
keep reflect^:2 ,:1 1 1 1
0 1 1 1
1 0 1 1
0 1 0 1
0 0 1 0
Only 9 weights are valid, here, from the second iteration of reflect.
Keep essentially performs two tests:
One test is a test for being zero
=&0
The other test is a test for significant items being positive:
(0 < ^&mask"2)
Remember that mask we made earlier? Its 1s and 0s. And
we are using them as exponents. Any number to the 0 power
is 1, and any number to the 1 power is 1. So this means that
we are only testing the sign of numbers in the upper right triangles
of the matrix which makes up the result of reflect.
Finally, we combine these two tests using:
>&(*./"1)
This consists of two parts. First we combine every
element in every row of both the left and right arguments
(*./"1). If they all are 1 we get a 1 and if any are 0 we get a 0.
Second, we want a 1 from the left argument (all significant
elements must be positive) and we want a 0 from the right
argument (at least one element must be non-zero). Since
1 is greater than 0 we get a 1 for the cases we want to keep.
Finally, we use keep on the result of reflect, with
#&(,/)
The ,/ bit means we are combining the first two dimensions.
(And the & part means we are applying this operation to both
the left and right arguments.)
So, basically, # is selecting rows from reflect that correspond
to 1s from keep.
And this result gets assigned to currentLevel (and will be
appended to weights).
And the condition in the while loop is a count of the number
of items in currentLevel. So the loop will repeat until currentLevel
is empty.
Finally, the result of a J explicit definition was the result of
the last line that executed which was not in a control block.
The way I have structured the code, the last line executed
in the verb will alway be an assignment to 'weights'. So the
result of the function will always be the last value which was
assigned to weights.
Hopefully I have not made mistakes here in my description.
However, it is possible I have, so please do not hesitate to
ask if something I said seems nonsensical or unclear.
--
Raul
----------------------------------------------------------------------
For information about J forums see http://www.jsoftware.com/forums.htm