Task 2: Submatrix Sum Submitted by: Jorg Sommrey You are given a NxM matrix A of integers. Write a script to construct a (N-1)x(M-1) matrix B having elements that are the sum over the 2x2 submatrices of A, b[i,k] = a[i,k] + a[i,k+1] + a[i+1,k] + a[i+1,k+1] Example 1 Input: $a = [ [1, 2, 3, 4], [5, 6, 7, 8], [9, 10, 11, 12] ] Output: $b = [ [14, 18, 22], [30, 34, 38] ] #### sub sms_WxH_Perl_4loops( $m, $w, $h ) { my ( $W, $H ) = ( scalar $m-> [0]-> @*, scalar @$m ); my @ret; for my $i ( 0 .. $H - $h ) { for my $j ( 0 .. $W - $w ) { for my $k ( 0 .. $w - 1 ) { for my $l ( 0 .. $h - 1 ) { $ret[ $i ][ $j ] += $m-> [ $i + $l ][ $j + $k ]; } } } } return \@ret } sub sms_WxH_Perl_3loops( $m, $w, $h ) { my ( $W, $H ) = ( scalar $m-> [0]-> @*, scalar @$m ); my ( @tmp, @ret ); for my $i ( 0 .. $H - 1 ) { for my $j ( 0 .. $W - $w ) { for my $k ( 0 .. $w - 1 ) { $tmp[ $i ][ $j ] += $m-> [ $i ][ $j + $k ]; } next if $i < $h - 1; for my $k ( 0 .. $h - 1 ) { $ret[ $i - $h + 1 ][ $j ] += $tmp[ $i - $k ][ $j ]; } } } return \@ret } $m = [ map [ map rand, 0 .. 149 ], 0 .. 149 ]; __END__ + Time (s) vs. N (NxN submatrix, 150x150 matrix) +----------------------------------------------------------+ |+ + + + + + + + | 10 |-+ +-| |+ +| |+ +| |+ A A A A +| |+ A A +| |+ A A +| | A A | | | 1 |-+ A +-| |+ A +| |+ +| |+ +| |+ A +| | A | |+ +| | B B B B B B | 0.1 |-+ B B B +-| |+ B +| |+ B B +| |+ B +| |+ +| | | |+ B +| | | 0.01 |-+ +-| |+ + + + + + + + +| +----------------------------------------------------------+ 0 20 40 60 80 100 120 140 sms_WxH_Perl_4loops A sms_WxH_Perl_3loops B +-----+-------+-------+ | N | A | B | +-----+-------+-------+ | 10 | 0.250 | 0.053 | | 20 | 0.809 | 0.091 | | 30 | 1.528 | 0.116 | | 40 | 2.288 | 0.134 | | 50 | 2.988 | 0.144 | | 60 | 3.512 | 0.147 | | 70 | 3.828 | 0.144 | | 80 | 3.862 | 0.134 | | 90 | 3.628 | 0.122 | | 100 | 3.122 | 0.103 | | 110 | 2.453 | 0.088 | | 120 | 1.675 | 0.066 | | 130 | 0.900 | 0.044 | | 140 | 0.284 | 0.022 | +-----+-------+-------+ #### sub sms_2x2_PDL ( $m ) { $m-> slice( '0:-2', '0:-2' ) + $m-> slice( '1:-1', '0:-2' ) + $m-> slice( '0:-2', '1:-1' ) + $m-> slice( '1:-1', '1:-1' ) } #### sub sms_WxH_PDL_naive ( $m, $w, $h ) { my $ret = 0; for my $r ( 0 .. $h - 1 ) { my $r_sel = "$r:" . ( $r - $h ); for my $c ( 0 .. $w - 1 ) { $ret += $m-> slice( "$c:" . ( $c - $w ), $r_sel ) } } return $ret } #### use Inline Pdlpp => <<'EOPP'; pp_def('sms_WxH_pdlpp_4loops', Pars => 'a(m,n); [o]b(p,q);', OtherPars => 'PDL_Indx par_sm_w; PDL_Indx par_sm_h;', RedoDimsCode => '$SIZE(p) = $SIZE(m) - $COMP(par_sm_w) + 1; $SIZE(q) = $SIZE(n) - $COMP(par_sm_h) + 1;', Code => ' PDL_Indx i, j, k, l, w, h, W, H; w = $COMP(par_sm_w); h = $COMP(par_sm_h); W = $SIZE(m); H = $SIZE(n); for (i = 0; i < H - h + 1; i++) { for (j = 0; j < W - w + 1; j++) { for (k = 0; k < w; k++) { for (l = 0; l < h; l++) { $b(p=>j, q=>i) += $a(m=>j+k, n=>i+l); } } } } ', ); pp_def('sms_WxH_pdlpp_3loops', Pars => 'a(m,n); [o]b(p,q); [t]t(p,n)', OtherPars => 'PDL_Indx par_sm_w; PDL_Indx par_sm_h;', RedoDimsCode => '$SIZE(p) = $SIZE(m) - $COMP(par_sm_w) + 1; $SIZE(q) = $SIZE(n) - $COMP(par_sm_h) + 1;', Code => ' PDL_Indx i, j, k, w, h, W, H; w = $COMP(par_sm_w); h = $COMP(par_sm_h); W = $SIZE(m); H = $SIZE(n); for (i = 0; i < H; i++) { for (j = 0; j < W - w + 1; j++) { for (k = 0; k < w; k++) { $t(p=>j, n=>i) += $a(m=>j+k, n=>i); } if (i >= h - 1) { for (k = 0; k < h; k++) { $b(p=>j, q=>i-h+1) += $t(p=>j, n=>i-k); } } } } ', ); EOPP #### sub sms_WxH_PDL_lags ( $m, $w, $h ) { my ( $W, $H ) = $m-> dims; return $m -> lags( 0, 1, 1 + $W - $w ) -> sumover -> lags( 1, 1, 1 + $H - $h ) -> xchg( 0, 1 ) -> sumover -> slice( '-1:0,-1:0' ) } $m = 1 + sequence 4, 3; print $m, sms_WxH_PDL_lags( $m, 2, 2 ), sms_WxH_PDL_lags( $m, 3, 3 ), sms_WxH_PDL_lags( $m, 4, 3 ), sms_WxH_PDL_lags( $m, 1, 1 ); __END__ [ [ 1 2 3 4] [ 5 6 7 8] [ 9 10 11 12] ] [ [14 18 22] [30 34 38] ] [ [54 63] ] [ [78] ] [ [ 1 2 3 4] [ 5 6 7 8] [ 9 10 11 12] ] #### + Time (s) vs. N (NxN submatrix, PDL: Double D [1500,1500] matrix) +-----------------------------------------------------------+ 2 |-+ + + + + + + + + +-| | C C | | C | | C | | D D D D C | 1.5 |-+ A C +-| | D D | | C | | C D | | | | B D D | 1 |-+ +-| | C | | C | | D D | | | | A D | 0.5 |-+ C +-| | D | | | | B D | | | | DC | 0 |-+ DD +-| | + + + + + + + + | +-----------------------------------------------------------+ 0 200 400 600 800 1000 1200 1400 sms_WxH_PDL_naive A sms_WxH_pdlpp_4loops B sms_WxH_pdlpp_3loops C sms_WxH_PDL_lags D +------+-------+-------+-------+-------+ | N | A | B | C | D | +------+-------+-------+-------+-------+ | 2 | 0.038 | 0.009 | 0.009 | 0.047 | | 4 | 0.294 | 0.031 | 0.019 | 0.041 | | 6 | 0.572 | 0.072 | 0.019 | 0.044 | | 10 | 1.531 | 0.250 | 0.031 | 0.031 | | 20 | | 1.150 | 0.072 | 0.022 | | 100 | | | 0.494 | 0.394 | | 200 | | | 0.931 | 0.797 | | 300 | | | 1.263 | 1.153 | | 400 | | | 1.544 | 1.431 | | 500 | | | 1.756 | 1.594 | | 600 | | | 1.869 | 1.653 | | 700 | | | 1.872 | 1.641 | | 800 | | | 1.788 | 1.594 | | 900 | | | 1.597 | 1.481 | | 1000 | | | 1.362 | 1.288 | | 1100 | | | 1.144 | 1.081 | | 1200 | | | 0.841 | 0.809 | | 1300 | | | 0.575 | 0.572 | | 1400 | | | 0.291 | 0.272 | +------+-------+-------+-------+-------+ #### sub _do_dimension ( $m, $w ) { $m -> slice([ 0, $w - 1 ], []) -> sumover -> slice( '*' ) -> append( $w >= ( $m-> dims )[ 0 ] ? empty : $m-> slice([ $w, -1 ], []) - $m-> slice([ 0, -1 - $w ], [])) -> cumusumover -> xchg( 0, 1 ) } sub sms_WxH_PDL_sliding ( $m, $w, $h ) { _do_dimension _do_dimension( $m, $w ), $h } $m = 1 + sequence 4, 3; print $m, sms_WxH_PDL_sliding( $m, 2, 2 ), sms_WxH_PDL_sliding( $m, 3, 3 ), sms_WxH_PDL_sliding( $m, 4, 3 ), sms_WxH_PDL_sliding( $m, 1, 1 ); __END__ # same (correct) output as above, skipped + Time (s) vs. N (NxN submatrix, PDL: Double D [1500,1500] matrix) +-----------------------------------------------------------+ | + + + + + + + + | | A | | A | | A A | 1.5 |-+ A +-| | A | | | | A | | | | A | | A | 1 |-+ +-| | | | A | | A | | | | | | A | 0.5 |-+ +-| | | | A | | A | | | | B B | | BB B B B B B B B B B | 0 |-+ B B B B +-| | + + + + + + + + | +-----------------------------------------------------------+ 0 200 400 600 800 1000 1200 1400 sms_WxH_PDL_lags A sms_WxH_PDL_sliding B +------+-------+-------+ | N | A | B | +------+-------+-------+ | 2 | 0.025 | 0.100 | | 4 | 0.034 | 0.075 | | 6 | 0.016 | 0.084 | | 10 | 0.034 | 0.091 | | 20 | 0.028 | 0.084 | | 100 | 0.369 | 0.081 | | 200 | 0.800 | 0.078 | | 300 | 1.122 | 0.116 | | 400 | 1.416 | 0.050 | | 500 | 1.572 | 0.053 | | 600 | 1.644 | 0.022 | | 700 | 1.675 | 0.025 | | 800 | 1.587 | 0.019 | | 900 | 1.456 | 0.019 | | 1000 | 1.297 | 0.009 | | 1100 | 1.047 | 0.009 | | 1200 | 0.813 | 0.019 | | 1300 | 0.566 | 0.006 | | 1400 | 0.275 | 0.003 | +------+-------+-------+ #### use PDL::Apply 'apply_rolling'; sub sms_WxH_PDL_ar ( $m, $w, $h ) { $m-> apply_rolling( $w, 'sum' ) -> slice( [ $w - 1, -1 ], [] ) -> xchg( 0, 1 ) -> apply_rolling( $h, 'sum' ) -> slice( [ $h - 1, -1 ], [] ) -> xchg( 0, 1 ) } my $m = random 150, 150; $t = time; sms_WxH_PDL_ar( $m, 2, 2 ); say time - $t; $t = time; sms_WxH_PDL_ar( $m, 10, 10 ); say time - $t; $t = time; sms_WxH_PDL_ar( $m, 75, 75 ); say time - $t; __END__ 3.36784410476685 3.20836806297302 1.28170394897461