http://qs321.pair.com?node_id=1023967

wanna_code_perl has asked for the wisdom of the Perl Monks concerning the following question:

Hello Monks

I'm new to PDL, giving it a whirl to speed up some 2D matrix crunching at the heart of an algorithm that has been rigorously proven to be the most efficient waste use of a week's worth of CPU time to solve the given problem. But I digress. :-)

I'm having the most trouble fully wrapping my head around threading, specifically how to fetch adjacent cells in a 2D matrix which match an arbitrary boolean expression.

Below follow some (annotated) pdl> commands hopefully showing what I'm trying to accomplish.

# Set up initial 4x4 matrix with some arbitrary data pdl> p $b = pdl (10..25)->reshape(4,4); [ [10 11 12 13] [14 15 16 17] [18 19 20 21] [22 23 24 25] ] # Fetch the 2D index for the position of element with value 21 pdl> p $pos = whichND($b == 21); [ [3 2] ] # Simple transformation matrix to mark adjacent cells. pdl> $xfrm = pdl [-1,0],[0,-1],[1,0],[0,1]; # Show the results of the transformation: So far so good (even though # one of the values is out of bounds, we're expecting that.) pdl> p $pos + $xfrm; [ [2 2] [3 1] [4 2] [3 3] ] # Here's the rub. The following command returns the *values* of # the adjacent cells, but I want the indicies instead. (See discussion +) pdl> p $b->indexND($pos + $xfrm, 'truncate'); [20 17 0 25] # $b->range($pos + $xfrm, 1, 'truncate') gives similar results

What I need:

The above code gets me halfway to where I need to be; from the $pos + $xfrm line, I need to basically grep those elements which are a) valid (i.e., within the piddle dimensions), and b) match a given boolean expression. Hence I tried this (obviously broken) code:

pdl> p $indicies = $b->indexND($pos + $xfrm, 'truncate')->which($b % 2 +); # Returns: empty # # Expected: Should contain two single element piddles: # [17 25] # OR, the indicies in the original matrix would be fine: # [ # [3 1] # 17 # [3 3] # 25 # ] # N.b. [2 2] would be omitted (value = 20, even), as would [4 2] (inde +x out of bounds)

I'm thinking this sort of thing must be a very common pattern, but I can't seem to find a workable example anywhere. What's the most concise and efficient way to iterate over adjacent cells matching a given boolean expression?

Replies are listed 'Best First'.
Re: Can't figure out simple transformation with PDL
by etj (Deacon) on May 22, 2022 at 18:34 UTC
    I believe the most important difficulty faced here is in trying to mix n-dimension slice-type operations (ending "ND": whichND, indexND, whereND) with single-dimension slice-type ops (which, index, where). What you call "threading" (now known as "broadcasting", in line with other array-programming environments) isn't really at play here, other than the (quite correct for your application) use of n-dimensional ops.

    It's possible to switch between the two modes of indexing an ndarray using https://metacpan.org/pod/PDL::Primitive#one2nd.

    To answer your last question: depending on what kind of iterating you have in mind, your general approach of supplying what looks to me like a "kernel" (+/-1 on the various dims) to add to the results of a whichND looks sound. Doing all the things you want to happen in a single PDL operation (on data that holds all the operands) is what I believe is "the PDL way".

Re: Can't figure out simple transformation with PDL
by syphilis (Archbishop) on Mar 19, 2013 at 08:28 UTC
    The best way to ensure that you get a good (and prompt) answer to questions relating to PDL is to ask those questions on the perldl mailing list.

    You'll *sometimes* get a good answer right here on perlmonks ... but that depends upon who is present in the monastery.

    Cheers,
    Rob
Re: Can't figure out simple transformation with PDL
by fglock (Vicar) on Mar 19, 2013 at 09:17 UTC

    I'm starting with your original statements, and then setting the selected elements to '-1' here:

    perldl> p $b = pdl (10..25)->reshape(4,4); [ [10 11 12 13] [14 15 16 17] [18 19 20 21] [22 23 24 25] ] perldl> p $pos = whichND($b == 21); [ [3 2] ] perldl> $xfrm = pdl [-1,0],[0,-1],[1,0],[0,1]; perldl> p $around = $b->indexND($pos + $xfrm, 'truncate'); [20 17 0 25] perldl> p $odd = $around->where( $around % 2 ); [17 25] perldl> $odd .= -1; # set original matrix through dataflow perldl> p $b [ [10 11 12 13] [14 15 16 -1] # odd elements around 21 are set to -1 [18 19 20 21] [22 23 24 -1] ]