Core.pm (PDL-2.074) | : | Core.pm (PDL-2.075) | ||
---|---|---|---|---|

skipping to change at line 27 | skipping to change at line 27 | |||

use Scalar::Util 'blessed'; | use Scalar::Util 'blessed'; | |||

# If quad (q/Q) is available for pack(). | # If quad (q/Q) is available for pack(). | |||

our $CAN_PACK_QUAD = !! eval { my $packed = pack "Q", 0; 1 }; | our $CAN_PACK_QUAD = !! eval { my $packed = pack "Q", 0; 1 }; | |||

# If "D" is available for pack(). | # If "D" is available for pack(). | |||

our $CAN_PACK_D = !! eval { my $packed = pack "D", 0; 1 }; | our $CAN_PACK_D = !! eval { my $packed = pack "D", 0; 1 }; | |||

our @EXPORT = qw( piddle pdl null barf ); # Only stuff always exported! | our @EXPORT = qw( piddle pdl null barf ); # Only stuff always exported! | |||

my @convertfuncs = map $_->convertfunc, PDL::Types::types(); | my @convertfuncs = map $_->convertfunc, PDL::Types::types(); | |||

my @exports_internal = qw(howbig threadids topdl); | my @exports_internal = qw(howbig broadcastids topdl); | |||

my @exports_normal = (@EXPORT, | my @exports_normal = (@EXPORT, | |||

@convertfuncs, | @convertfuncs, | |||

qw(nelem dims shape null | qw(nelem dims shape null | |||

empty | ||||

convert inplace zeroes zeros ones nan inf i list listindices unpdl | convert inplace zeroes zeros ones nan inf i list listindices unpdl | |||

set at flows thread_define over reshape dog cat barf type | set at flows broadcast_define over reshape dog cat barf type | |||

dummy mslice approx flat sclr squeeze | thread_define dummy mslice approx flat sclr squeeze | |||

get_autopthread_targ set_autopthread_targ get_autopthread_actual | get_autopthread_targ set_autopthread_targ get_autopthread_actual | |||

get_autopthread_dim get_autopthread_size set_autopthread_size) ); | get_autopthread_dim get_autopthread_size set_autopthread_size) ); | |||

our @EXPORT_OK = (@exports_internal, @exports_normal); | our @EXPORT_OK = (@exports_internal, @exports_normal); | |||

our %EXPORT_TAGS = ( | our %EXPORT_TAGS = ( | |||

Func => [@exports_normal], | Func => [@exports_normal], | |||

Internal => [@exports_internal] ); | Internal => [@exports_internal] ); | |||

our ($level, @dims, $sep, $sep2, $match); | our ($level, @dims, $sep, $sep2, $match); | |||

# Important variables (place in PDL namespace) | # Important variables (place in PDL namespace) | |||

skipping to change at line 60 | skipping to change at line 61 | |||

$PDL::use_commas = 0; # Whether to insert commas when printing arrays | $PDL::use_commas = 0; # Whether to insert commas when printing arrays | |||

$PDL::floatformat = "%7g"; # Default print format for long numbers | $PDL::floatformat = "%7g"; # Default print format for long numbers | |||

$PDL::doubleformat = "%10.8g"; | $PDL::doubleformat = "%10.8g"; | |||

$PDL::indxformat = "%12d"; # Default print format for PDL_Indx values | $PDL::indxformat = "%12d"; # Default print format for PDL_Indx values | |||

$PDL::undefval = 0; # Value to use instead of undef when creating PDL s | $PDL::undefval = 0; # Value to use instead of undef when creating PDL s | |||

$PDL::toolongtoprint = 10000; # maximum pdl size to stringify for printing | $PDL::toolongtoprint = 10000; # maximum pdl size to stringify for printing | |||

################ Exportable functions of the Core ###################### | ################ Exportable functions of the Core ###################### | |||

*at_c = *at_bad_c; # back-compat alias | *at_c = *at_bad_c; # back-compat alias | |||

*thread_define = *broadcast_define; | ||||

*PDL::threadover_n = *PDL::broadcastover_n; | ||||

*howbig = \&PDL::howbig; *unpdl = \&PDL::unpdl; | *howbig = \&PDL::howbig; *unpdl = \&PDL::unpdl; | |||

*nelem = \&PDL::nelem; *inplace = \&PDL::inplace; | *nelem = \&PDL::nelem; *inplace = \&PDL::inplace; | |||

*dims = \&PDL::dims; *list = \&PDL::list; | *dims = \&PDL::dims; *list = \&PDL::list; | |||

*threadids = \&PDL::threadids; *listindices = \&PDL::listindices; | *broadcastids = \&PDL::broadcastids; *listindices = \&PDL::listindices; | |||

*null = \&PDL::null; *set = \&PDL::set; | *null = \&PDL::null; *set = \&PDL::set; | |||

*at = \&PDL::at; *flows = \&PDL::flows; | *at = \&PDL::at; *flows = \&PDL::flows; | |||

*sclr = \&PDL::sclr; *shape = \&PDL::shape; | *sclr = \&PDL::sclr; *shape = \&PDL::shape; | |||

for my $t (PDL::Types::types()) { | for my $t (PDL::Types::types()) { | |||

my $conv = $t->convertfunc; | my $conv = $t->convertfunc; | |||

no strict 'refs'; | no strict 'refs'; | |||

*$conv = *{"PDL::$conv"} = sub { | *$conv = *{"PDL::$conv"} = sub { | |||

return $t unless @_; | return $t unless @_; | |||

alltopdl('PDL', (@_>1 ? [@_] : shift), $t); | alltopdl('PDL', (@_>1 ? [@_] : shift), $t); | |||

}; | }; | |||

} | } | |||

BEGIN { | BEGIN { | |||

*thread_define = \&PDL::thread_define; | *broadcast_define = \&PDL::broadcast_define; | |||

*convert = \&PDL::convert; *over = \&PDL::over; | *convert = \&PDL::convert; *over = \&PDL::over; | |||

*dog = \&PDL::dog; *cat = \&PDL::cat; | *dog = \&PDL::dog; *cat = \&PDL::cat; | |||

*type = \&PDL::type; *approx = \&PDL::approx; | *type = \&PDL::type; *approx = \&PDL::approx; | |||

*dummy = \&PDL::dummy; | *dummy = \&PDL::dummy; | |||

*mslice = \&PDL::mslice; | *mslice = \&PDL::mslice; | |||

*isempty = \&PDL::isempty; | *isempty = \&PDL::isempty; | |||

*string = \&PDL::string; | *string = \&PDL::string; | |||

} | } | |||

=head1 NAME | =head1 NAME | |||

PDL::Core - fundamental PDL functionality and vectorization/threading | PDL::Core - fundamental PDL functionality and vectorization/broadcasting | |||

=head1 DESCRIPTION | =head1 DESCRIPTION | |||

Methods and functions for type conversions, PDL creation, | Methods and functions for type conversions, PDL creation, | |||

type conversion, threading etc. | type conversion, broadcasting etc. | |||

=head1 SYNOPSIS | =head1 SYNOPSIS | |||

use PDL::Core; # Normal routines | use PDL::Core; # Normal routines | |||

use PDL::Core ':Internal'; # Hairy routines | use PDL::Core ':Internal'; # Hairy routines | |||

=head1 VECTORIZATION/THREADING: METHOD AND NOMENCLATURE | =head1 VECTORIZATION/BROADCASTING: METHOD AND NOMENCLATURE | |||

PDL provides vectorized operations via a built-in engine. | PDL provides vectorized operations via a built-in engine. | |||

Vectorization is called "threading" for historical reasons. | Vectorization in PDL is called "broadcasting" (formerly, up to 2.075, "threading | |||

The threading engine implements simple rules for each operation. | "). | |||

The broadcasting engine implements simple rules for each operation. | ||||

Each PDL object has a "shape" that is a generalized N-dimensional | Each PDL object has a "shape" that is a generalized N-dimensional | |||

rectangle defined by a "dim list" of sizes in an arbitrary | rectangle defined by a "dim list" of sizes in an arbitrary | |||

set of dimensions. A PDL with shape 2x3 has 6 elements and is | set of dimensions. A PDL with shape 2x3 has 6 elements and is | |||

said to be two-dimensional, or may be referred to as a 2x3-PDL. | said to be two-dimensional, or may be referred to as a 2x3-PDL. | |||

The dimensions are indexed numerically starting at 0, so a | The dimensions are indexed numerically starting at 0, so a | |||

2x3-PDL has a dimension 0 (or "dim 0") with size 2 and a 1 dimension | 2x3-PDL has a dimension 0 (or "dim 0") with size 2 and a 1 dimension | |||

(or "dim 1") with size 3. | (or "dim 1") with size 3. | |||

PDL generalizes *all* mathematical operations with the notion of | PDL generalizes *all* mathematical operations with the notion of | |||

"active dims": each operator has zero or more active dims that are | "active dims": each operator has zero or more active dims that are | |||

used in carrying out the operation. Simple scalar operations like | used in carrying out the operation. Simple scalar operations like | |||

scalar multiplication ('*') have 0 active dims. More complicated | scalar multiplication ('*') have 0 active dims. More complicated | |||

operators can have more active dims. For example, matrix | operators can have more active dims. For example, matrix | |||

multiplication ('x') has 2 active dims. Additional dims are | multiplication ('x') has 2 active dims. Additional dims are | |||

automatically vectorized across -- e.g. multiplying a 2x5-PDL with a | automatically vectorized across -- e.g. multiplying a 2x5-PDL with a | |||

2x5-PDL requires 10 simple multiplication operations, and yields a | 2x5-PDL requires 10 simple multiplication operations, and yields a | |||

2x5-PDL result. | 2x5-PDL result. | |||

=head2 Threading rules | =head2 Broadcasting rules | |||

In any PDL expression, the active dims appropriate for each operator | In any PDL expression, the active dims appropriate for each operator | |||

are used starting at the 0 dim and working forward through the dim | are used starting at the 0 dim and working forward through the dim | |||

list of each object. All additional dims after the active dims are | list of each object. All additional dims after the active dims are | |||

"thread dims". The thread dims do not have to agree exactly: they are | "broadcast dims". The broadcast dims do not have to agree exactly: they are | |||

coerced to agree according to simple rules: | coerced to agree according to simple rules: | |||

=over 3 | =over 3 | |||

=item * Null PDLs match any dim list (see below). | =item * Null PDLs match any dim list (see below). | |||

=item * Dims with sizes other than 1 must all agree in size. | =item * Dims with sizes other than 1 must all agree in size. | |||

=item * Dims of size 1 are silently repeated as necessary except for C<[phys]> P DLs. | =item * Dims of size 1 are silently repeated as necessary except for C<[phys]> P DLs. | |||

skipping to change at line 178 | skipping to change at line 181 | |||

Any dim of a PDL can be set explicitly to size 0. If so, the PDL | Any dim of a PDL can be set explicitly to size 0. If so, the PDL | |||

contains zero values (because the total number of values is the | contains zero values (because the total number of values is the | |||

product of all the sizes in the PDL's shape or dimlist). | product of all the sizes in the PDL's shape or dimlist). | |||

Scalar PDLs are zero-dimensional and have no entries in the dim list, | Scalar PDLs are zero-dimensional and have no entries in the dim list, | |||

so they cannot be empty. 1-D and higher PDLs can be empty. Empty | so they cannot be empty. 1-D and higher PDLs can be empty. Empty | |||

PDLs are useful for set operations, and are most commonly encountered | PDLs are useful for set operations, and are most commonly encountered | |||

in the output from selection operators such as L<which|PDL::Primitive> | in the output from selection operators such as L<which|PDL::Primitive> | |||

and L<whichND|PDL::Primitive>. Not all empty PDLs have the same | and L<whichND|PDL::Primitive>. Not all empty PDLs have the same | |||

threading properties -- e.g. a 2x0-PDL represents a collection of | broadcasting properties -- e.g. a 2x0-PDL represents a collection of | |||

2-vectors that happens to contain no elements, while a simple 0-PDL | 2-vectors that happens to contain no elements, while a simple 0-PDL | |||

represents a collection of scalar values (that also happens to contain | represents a collection of scalar values (that also happens to contain | |||

no elements). | no elements). | |||

Note that 0 dims are not adjustable via the threading rules -- a dim | Note that 0 dims are not adjustable via the broadcasting rules -- a dim | |||

with size 0 can only match a corresponding dim of size 0 or 1. | with size 0 can only match a corresponding dim of size 0 or 1. | |||

=head2 Thread rules and assignments | =head2 Broadcast rules and assignments | |||

Versions of PDL through 2.4.10 have some irregularity with threading and | Versions of PDL through 2.4.10 have some irregularity with broadcasting and | |||

assignments. Currently the threading engine performs a full expansion of | assignments. Currently the broadcasting engine performs a full expansion of | |||

both sides of the computed assignment operator C<.=> (which assigns values | both sides of the computed assignment operator C<.=> (which assigns values | |||

to a pre-existing PDL). This leads to counter-intuitive behavior in | to a pre-existing PDL). This leads to counter-intuitive behavior in | |||

some cases: | some cases: | |||

=over 3 | =over 3 | |||

=item * Generalized scalars and computed assignment | =item * Generalized scalars and computed assignment | |||

If the PDL on the left-hand side of C<.=> has a dim of size 1, it can be | If the PDL on the left-hand side of C<.=> has a dim of size 1, it can be | |||

treated as a generalized scalar, as in: | treated as a generalized scalar, as in: | |||

$x = sequence(2,3); | $x = sequence(2,3); | |||

$y = zeroes(1,3); | $y = zeroes(1,3); | |||

$y .= $x; | $y .= $x; | |||

In this case, C<$y> is automatically treated as a 2x3-PDL during the | In this case, C<$y> is automatically treated as a 2x3-PDL during the | |||

threading operation, but half of the values from C<$x> silently disappear. | broadcasting operation, but half of the values from C<$x> silently disappear. | |||

The output is, as Kernighan and Ritchie would say, "undefined". | The output is, as Kernighan and Ritchie would say, "undefined". | |||

Further, if the value on the right of C<.=> is empty, then C<.=> becomes | Further, if the value on the right of C<.=> is empty, then C<.=> becomes | |||

a silent no-op: | a silent no-op: | |||

$x = zeroes(0); | $x = zeroes(0); | |||

$y = zeroes(1); | $y = zeroes(1); | |||

$y .= $x+1; | $y .= $x+1; | |||

print $y; | print $y; | |||

skipping to change at line 460 | skipping to change at line 463 | |||

C<$PDL::undefval> defaults to zero. | C<$PDL::undefval> defaults to zero. | |||

As a final note, if you include an Empty PDL in the list of objects to | As a final note, if you include an Empty PDL in the list of objects to | |||

construct into a PDL, it is kept as a placeholder pane -- so if you feed | construct into a PDL, it is kept as a placeholder pane -- so if you feed | |||

in (say) 7 objects, you get a size of 7 in the 0th dim of the output PDL. | in (say) 7 objects, you get a size of 7 in the 0th dim of the output PDL. | |||

The placeholder panes are completely padded out. But if you feed in only | The placeholder panes are completely padded out. But if you feed in only | |||

a single Empty PDL, you get back the Empty PDL (no padding). | a single Empty PDL, you get back the Empty PDL (no padding). | |||

=cut | =cut | |||

=head2 empty | ||||

=for ref | ||||

Returns an empty ndarray, with a single zero-length dimension. | ||||

Only available as a function, not a method. | ||||

=for usage | ||||

$x = empty; # defaults to lowest type so it can always be promoted up | ||||

$x = empty(float); | ||||

=cut | ||||

sub empty { | ||||

my ($type) = @_; | ||||

$type //= 0; | ||||

PDL->new_from_specification(PDL::Type->new($type), 0); | ||||

} | ||||

=head2 null | =head2 null | |||

=for ref | =for ref | |||

Returns a 'null' ndarray. | Returns a 'null' ndarray. | |||

It is an error to pass one of these as an input to a function. | It is an error to pass one of these as an input to a function. | |||

=for usage | =for usage | |||

$x = null; | $x = null; | |||

skipping to change at line 509 | skipping to change at line 532 | |||

=head2 nullcreate | =head2 nullcreate | |||

=for ref | =for ref | |||

Returns a 'null' ndarray. | Returns a 'null' ndarray. | |||

=for usage | =for usage | |||

$x = PDL->nullcreate($arg) | $x = PDL->nullcreate($arg) | |||

This is an routine used by many of the threading primitives | This is an routine used by many of the broadcasting primitives | |||

(i.e. L<sumover|PDL::Ufunc/sumover>, | (i.e. L<sumover|PDL::Ufunc/sumover>, | |||

L<minimum|PDL::Ufunc/minimum>, etc.) to generate a null ndarray for the | L<minimum|PDL::Ufunc/minimum>, etc.) to generate a null ndarray for the | |||

function's output that will behave properly for derived (or | function's output that will behave properly for derived (or | |||

subclassed) PDL objects. | subclassed) PDL objects. | |||

For the above usage: | For the above usage: | |||

If C<$arg> is a PDL, or a derived PDL, then C<$arg-E<gt>null> is returned. | If C<$arg> is a PDL, or a derived PDL, then C<$arg-E<gt>null> is returned. | |||

If C<$arg> is a scalar (i.e. a zero-dimensional PDL) then C<PDL-E<gt>null> | If C<$arg> is a scalar (i.e. a zero-dimensional PDL) then C<PDL-E<gt>null> | |||

is returned. | is returned. | |||

skipping to change at line 771 | skipping to change at line 794 | |||

sub PDL::shape { # Return dimensions as a pdl | sub PDL::shape { # Return dimensions as a pdl | |||

indx([PDL->topdl(shift)->dims]); | indx([PDL->topdl(shift)->dims]); | |||

} | } | |||

sub PDL::howbig { | sub PDL::howbig { | |||

my $t = shift; | my $t = shift; | |||

if("PDL::Type" eq ref $t) {$t = $t->[0]} | if("PDL::Type" eq ref $t) {$t = $t->[0]} | |||

PDL::howbig_c($t); | PDL::howbig_c($t); | |||

} | } | |||

=head2 threadids | =head2 broadcastids | |||

=for ref | =for ref | |||

Returns the ndarray thread IDs as a perl list | Returns the ndarray broadcast IDs as a perl list | |||

Note that C<threadids()> is not exported by default (see example | Note that C<broadcastids()> is not exported by default (see example | |||

below for usage). | below for usage). | |||

=for usage | =for usage | |||

use PDL::Core ':Internal'; # use the internal routines of | use PDL::Core ':Internal'; # use the internal routines of | |||

# the Core module | # the Core module | |||

@ids = threadids $ndarray; | @ids = broadcastids $ndarray; | |||

=cut | =cut | |||

sub PDL::threadids { # Return dimensions as @list | sub PDL::broadcastids { | |||

PDL->topdl(shift)->threadids_c; | PDL->topdl(shift)->broadcastids_c; | |||

} | } | |||

################# Creation/copying functions ####################### | ################# Creation/copying functions ####################### | |||

sub piddle {PDL->pdl(@_)} | sub piddle {PDL->pdl(@_)} | |||

sub pdl {PDL->pdl(@_)} | sub pdl {PDL->pdl(@_)} | |||

sub PDL::pdl { my $x = shift; return $x->new(@_) } | sub PDL::pdl { shift->new(@_) } | |||

=head2 doflow | =head2 doflow | |||

=for ref | =for ref | |||

Turn on dataflow, forward only. This means any transformations (a.k.a. PDL | Turn on dataflow, forward only. This means any transformations (a.k.a. PDL | |||

operations) applied to this ndarray afterwards will have forward dataflow: | operations) applied to this ndarray afterwards will have forward dataflow: | |||

$x = sequence 3; | $x = sequence 3; | |||

$x->doflow; | $x->doflow; | |||

skipping to change at line 1172 | skipping to change at line 1195 | |||

return max($item->type->enum, $sofar) if UNIVERSAL::isa($item, 'PDL'); | return max($item->type->enum, $sofar) if UNIVERSAL::isa($item, 'PDL'); | |||

return $PDL_D if ref($item) ne 'ARRAY'; | return $PDL_D if ref($item) ne 'ARRAY'; | |||

# only need to check first item for an array of complex vals | # only need to check first item for an array of complex vals | |||

return $MAX_TYPE if _establish_type($item->[0], $sofar) == $MAX_TYPE; | return $MAX_TYPE if _establish_type($item->[0], $sofar) == $MAX_TYPE; | |||

# only need to recurse for items that are refs | # only need to recurse for items that are refs | |||

# as $sofar will be $PDL_D at a minimum | # as $sofar will be $PDL_D at a minimum | |||

max ($sofar, map _establish_type($_, $sofar), grep ref, @$item); | max ($sofar, map _establish_type($_, $sofar), grep ref, @$item); | |||

} | } | |||

sub PDL::new { | sub PDL::new { | |||

# print "in PDL::new\n"; | return $_[0]->copy if ref($_[0]); | |||

my $this = shift; | my $this = shift; | |||

return $this->copy if ref($this); | ||||

my $type = ref($_[0]) eq 'PDL::Type' ? shift->enum : undef; | my $type = ref($_[0]) eq 'PDL::Type' ? shift->enum : undef; | |||

my $value = (@_ >1 ? [@_] : shift); # ref thyself | my $value = (@_ > 1 ? [@_] : shift); | |||

unless(defined $value) { | unless(defined $value) { | |||

if($PDL::debug) { | if($PDL::debug) { | |||

print STDERR "Warning: PDL::new converted undef to \$PDL::undefval ($P DL::undefval)\n"; | print STDERR "Warning: PDL::new converted undef to \$PDL::undefval ($P DL::undefval)\n"; | |||

} | } | |||

$value = ($PDL::undefval//0)+0 | $value = ($PDL::undefval//0)+0 | |||

} | } | |||

$type //= ref($value) ? _establish_type($value, $PDL_D) : $PDL_D; | $type //= ref($value) ? _establish_type($value, $PDL_D) : $PDL_D; | |||

return pdl_avref($value,$this,$type) if ref($value) eq "ARRAY"; | return pdl_avref($value,$this,$type) if ref($value) eq "ARRAY"; | |||

my $new = $this->initialize(); | my $new = $this->initialize; | |||

$new->set_datatype($type); | $new->set_datatype($type); | |||

if (ref(\$value) eq "SCALAR") { | if (ref(\$value) eq "SCALAR") { | |||

# The string processing is extremely slow. Benchmarks indicated that it | # The string processing is extremely slow. Benchmarks indicated that it | |||

# takes 10x longer to process a scalar number compared with normal Perl | # takes 10x longer to process a scalar number compared with normal Perl | |||

# conversion of a string to a number. So, only use the string processing | # conversion of a string to a number. So, only use the string processing | |||

# if the input looks like a real string, i.e. it doesn't look like a plain | # if the input looks like a real string, i.e. it doesn't look like a plain | |||

# number. Note that for our purposes, looks_like_number incorrectly | # number. Note that for our purposes, looks_like_number incorrectly | |||

# handles the strings 'inf' and 'nan' on Windows machines. We want to send | # handles the strings 'inf' and 'nan' on Windows machines. We want to send | |||

# those to the string processing, so this checks for them in a way that | # those to the string processing, so this checks for them in a way that | |||

skipping to change at line 1249 | skipping to change at line 1271 | |||

Since C<$new = $old> just makes a new reference, the | Since C<$new = $old> just makes a new reference, the | |||

C<copy> method is provided to allow real independent | C<copy> method is provided to allow real independent | |||

copies to be made. | copies to be made. | |||

=cut | =cut | |||

sub PDL::copy { | sub PDL::copy { | |||

my $value = shift; | my $value = shift; | |||

barf("Argument is an ".ref($value)." not an object") unless blessed($value); | barf("Argument is an ".ref($value)." not an object") unless blessed($value); | |||

# threadI(-1,[]) is just an identity vafftrans with threadId copying ;) | return $value->nullcreate if $value->isnull; | |||

$value->threadI(-1,[])->sever; | # broadcastI(-1,[]) is just an identity vafftrans with broadcastid copying ; | |||

) | ||||

$value->broadcastI(-1,[])->sever; | ||||

} | } | |||

=head2 hdr_copy | =head2 hdr_copy | |||

=for ref | =for ref | |||

Return an explicit copy of the header of a PDL. | Return an explicit copy of the header of a PDL. | |||

hdr_copy is just a wrapper for the internal routine _hdr_copy, which | hdr_copy is just a wrapper for the internal routine _hdr_copy, which | |||

takes the hash ref itself. That is the routine which is used to make | takes the hash ref itself. That is the routine which is used to make | |||

skipping to change at line 1375 | skipping to change at line 1398 | |||

} | } | |||

$val; | $val; | |||

} | } | |||

=head2 unwind | =head2 unwind | |||

=for ref | =for ref | |||

Return an ndarray which is the same as the argument except | Return an ndarray which is the same as the argument except | |||

that all threadids have been removed. | that all broadcastids have been removed. | |||

=for usage | =for usage | |||

$y = $x->unwind; | $y = $x->unwind; | |||

=head2 make_physical | =head2 make_physical | |||

=for ref | =for ref | |||

Make sure the data portion of an ndarray can be accessed from XS code. | Make sure the data portion of an ndarray can be accessed from XS code. | |||

skipping to change at line 1413 | skipping to change at line 1436 | |||

might want to consider using the PDL preprocessor | might want to consider using the PDL preprocessor | |||

(see L<PDL::PP>) | (see L<PDL::PP>) | |||

which can be used to transparently access virtual ndarrays without the | which can be used to transparently access virtual ndarrays without the | |||

need to physicalise them (though there are exceptions). | need to physicalise them (though there are exceptions). | |||

=cut | =cut | |||

sub PDL::unwind { | sub PDL::unwind { | |||

my $value = shift; | my $value = shift; | |||

my $foo = $value->null(); | my $foo = $value->null(); | |||

$foo .= $value->unthread(); | $foo .= $value->unbroadcast(); | |||

return $foo; | return $foo; | |||

} | } | |||

=head2 dummy | =head2 dummy | |||

=for ref | =for ref | |||

Insert a 'dummy dimension' of given length (defaults to 1) | Insert a 'dummy dimension' of given length (defaults to 1) | |||

No relation to the 'Dungeon Dimensions' in Discworld! | No relation to the 'Dungeon Dimensions' in Discworld! | |||

skipping to change at line 1537 | skipping to change at line 1560 | |||

my $ndims = $this->getndims; | my $ndims = $this->getndims; | |||

my $targd = $ndims-1; | my $targd = $ndims-1; | |||

my @dimmark = (0..$ndims-1); | my @dimmark = (0..$ndims-1); | |||

barf "too many dimensions" if @dims > $ndims; | barf "too many dimensions" if @dims > $ndims; | |||

for my $dim (@dims) { | for my $dim (@dims) { | |||

barf "dimension index $dim larger than greatest dimension" | barf "dimension index $dim larger than greatest dimension" | |||

if $dim > $ndims-1 ; | if $dim > $ndims-1 ; | |||

$targd = $dim if $targd > $dim; | $targd = $dim if $targd > $dim; | |||

barf "duplicate dimension $dim" if $dimmark[$dim]++ > $dim; | barf "duplicate dimension $dim" if $dimmark[$dim]++ > $dim; | |||

} | } | |||

my $clumped = $this->thread(@dims)->unthread(0)->clump(scalar @dims); | my $clumped = $this->broadcast(@dims)->unbroadcast(0)->clump(scalar @dims); | |||

$clumped = $clumped->mv(0,$targd) if $targd > 0; | $clumped = $clumped->mv(0,$targd) if $targd > 0; | |||

return $clumped; | return $clumped; | |||

} | } | |||

=head2 thread_define | =head2 broadcast_define | |||

=for ref | =for ref | |||

define functions that support threading at the perl level | define functions that support broadcasting at the perl level | |||

=for example | =for example | |||

thread_define 'tline(a(n);b(n))', over { | broadcast_define 'tline(a(n);b(n))', over { | |||

line $_[0], $_[1]; # make line compliant with threading | line $_[0], $_[1]; # make line compliant with broadcasting | |||

}; | }; | |||

C<thread_define> provides some support for threading (see | C<broadcast_define> provides some support for broadcasting (see | |||

L<PDL::Indexing>) at the perl level. It allows you to do things for | L<PDL::Indexing>) at the perl level. It allows you to do things for | |||

which you normally would have resorted to PDL::PP (see L<PDL::PP>); | which you normally would have resorted to PDL::PP (see L<PDL::PP>); | |||

however, it is most useful to wrap existing perl functions so that the | however, it is most useful to wrap existing perl functions so that the | |||

new routine supports PDL threading. | new routine supports PDL broadcasting. | |||

C<thread_define> is used to define new I<threading aware> | C<broadcast_define> is used to define new I<broadcasting aware> | |||

functions. Its first argument is a symbolic repesentation of the new | functions. Its first argument is a symbolic repesentation of the new | |||

function to be defined. The string is composed of the name of the new | function to be defined. The string is composed of the name of the new | |||

function followed by its signature (see L<PDL::Indexing> and L<PDL::PP>) | function followed by its signature (see L<PDL::Indexing> and L<PDL::PP>) | |||

in parentheses. The second argument is a subroutine that will be | in parentheses. The second argument is a subroutine that will be | |||

called with the slices of the actual runtime arguments as specified by | called with the slices of the actual runtime arguments as specified by | |||

its signature. Correct dimension sizes and minimal number of | its signature. Correct dimension sizes and minimal number of | |||

dimensions for all arguments will be checked (assuming the rules of | dimensions for all arguments will be checked (assuming the rules of | |||

PDL threading, see L<PDL::Indexing>). | PDL broadcasting, see L<PDL::Indexing>). | |||

The actual work is done by the C<signature> class which parses the signature | The actual work is done by the C<signature> class which parses the signature | |||

string, does runtime dimension checks and the routine C<threadover> that | string, does runtime dimension checks and the routine C<broadcastover> that | |||

generates the loop over all appropriate slices of pdl arguments and creates | generates the loop over all appropriate slices of pdl arguments and creates | |||

pdls as needed. | pdls as needed. | |||

Similar to C<pp_def> and its C<OtherPars> option it is possible to | Similar to C<pp_def> and its C<OtherPars> option it is possible to | |||

define the new function so that it accepts normal perl args as well as | define the new function so that it accepts normal perl args as well as | |||

ndarrays. You do this by using the C<NOtherPars> parameter in the | ndarrays. You do this by using the C<NOtherPars> parameter in the | |||

signature. The number of C<NOtherPars> specified will be passed | signature. The number of C<NOtherPars> specified will be passed | |||

unaltered into the subroutine given as the second argument of | unaltered into the subroutine given as the second argument of | |||

C<thread_define>. Let's illustrate this with an example: | C<broadcast_define>. Let's illustrate this with an example: | |||

PDL::thread_define 'triangles(inda();indb();indc()), NOtherPars => 2', | PDL::broadcast_define 'triangles(inda();indb();indc()), NOtherPars => 2', | |||

PDL::over { | PDL::over { | |||

${$_[3]} .= $_[4].join(',',map {$_->at} @_[0..2]).",-1,\n"; | ${$_[3]} .= $_[4].join(',',map {$_->at} @_[0..2]).",-1,\n"; | |||

}; | }; | |||

This defines a function C<triangles> that takes 3 ndarrays as input | This defines a function C<triangles> that takes 3 ndarrays as input | |||

plus 2 arguments which are passed into the routine unaltered. This routine | plus 2 arguments which are passed into the routine unaltered. This routine | |||

is used to collect lists of indices into a perl scalar that is passed by | is used to collect lists of indices into a perl scalar that is passed by | |||

reference. Each line is preceded by a prefix passed as C<$_[4]>. Here is | reference. Each line is preceded by a prefix passed as C<$_[4]>. Here is | |||

typical usage: | typical usage: | |||

skipping to change at line 1616 | skipping to change at line 1639 | |||

Currently, this is probably not much more than a POP (proof of principle) | Currently, this is probably not much more than a POP (proof of principle) | |||

but is hoped to be useful enough for some real life work. | but is hoped to be useful enough for some real life work. | |||

Check L<PDL::PP> for the format of the signature. Currently, the | Check L<PDL::PP> for the format of the signature. Currently, the | |||

C<[t]> qualifier and all type qualifiers are ignored. | C<[t]> qualifier and all type qualifiers are ignored. | |||

=cut | =cut | |||

sub PDL::over (&) { $_[0] } | sub PDL::over (&) { $_[0] } | |||

sub PDL::thread_define ($$) { | sub PDL::broadcast_define ($$) { | |||

require PDL::PP::Signature; | require PDL::PP::Signature; | |||

my ($str,$sub) = @_; | my ($str,$sub) = @_; | |||

my $others = 0; | my $others = 0; | |||

if ($str =~ s/[,]*\s*NOtherPars\s*=>\s*([0-9]+)\s*[,]*//) {$others = $1} | if ($str =~ s/[,]*\s*NOtherPars\s*=>\s*([0-9]+)\s*[,]*//) {$others = $1} | |||

barf "invalid string $str" unless $str =~ /\s*([^(]+)\((.+)\)\s*$/x; | barf "invalid string $str" unless $str =~ /\s*([^(]+)\((.+)\)\s*$/x; | |||

my ($name,$sigstr) = ($1,$2); | my ($name,$sigstr) = ($1,$2); | |||

print "defining '$name' with signature '$sigstr' and $others extra args\n" | print "defining '$name' with signature '$sigstr' and $others extra args\n" | |||

if $PDL::debug; | if $PDL::debug; | |||

my $sig = PDL::PP::Signature->new($sigstr); | my $sig = PDL::PP::Signature->new($sigstr); | |||

my $args = @{$sig->names}; # number of ndarray arguments | my $args = @{$sig->names}; # number of ndarray arguments | |||

barf "no ndarray args" if $args == 0; | barf "no ndarray args" if $args == 0; | |||

$args--; | $args--; | |||

# TODO: $sig->dimcheck(@_) + proper creating generation | # TODO: $sig->dimcheck(@_) + proper creating generation | |||

my $package = caller; | my $package = caller; | |||

print "defining... $name\n" if $PDL::debug; | print "defining... $name\n" if $PDL::debug; | |||

no strict 'refs'; | no strict 'refs'; | |||

*{"$package\::$name"} = sub { | *{"$package\::$name"} = sub { | |||

@_[0..$args] = map PDL::Core::topdl($_), @_[0..$args]; | @_[0..$args] = map PDL::Core::topdl($_), @_[0..$args]; | |||

$sig->checkdims(@_); | $sig->checkdims(@_); | |||

PDL::threadover($others,@_,$sig->realdims,$sig->creating,$sub); | PDL::broadcastover($others,@_,$sig->realdims,$sig->creating,$sub); | |||

}; | }; | |||

} | } | |||

=head2 thread | =head2 broadcast | |||

=for ref | =for ref | |||

Use explicit threading over specified dimensions (see also L<PDL::Indexing>) | Use explicit broadcasting over specified dimensions (see also L<PDL::Indexing>) | |||

=for usage | =for usage | |||

$y = $x->thread($dim,[$dim1,...]) | $y = $x->broadcast($dim,[$dim1,...]) | |||

=for example | =for example | |||

$x = zeroes 3,4,5; | $x = zeroes 3,4,5; | |||

$y = $x->thread(2,0); | $y = $x->broadcast(2,0); | |||

Same as L</thread1>, i.e. uses thread id 1. | Same as L</broadcast1>, i.e. uses broadcast id 1. | |||

=cut | =cut | |||

sub PDL::thread { | sub PDL::broadcast { | |||

my $var = shift; | my $var = shift; | |||

$var->threadI(1,\@_); | $var->broadcastI(1,\@_); | |||

} | } | |||

=head2 thread1 | =head2 broadcast1 | |||

=for ref | =for ref | |||

Explicit threading over specified dims using thread id 1. | Explicit broadcasting over specified dims using broadcast id 1. | |||

=for usage | =for usage | |||

$xx = $x->thread1(3,1) | $xx = $x->broadcast1(3,1) | |||

=for example | =for example | |||

Wibble | Wibble | |||

Convenience function interfacing to | Convenience function interfacing to | |||

L<PDL::Slices::threadI|PDL::Slices/threadI>. | L<PDL::Slices::broadcastI|PDL::Slices/broadcastI>. | |||

=cut | =cut | |||

sub PDL::thread1 { | sub PDL::broadcast1 { | |||

my $var = shift; | my $var = shift; | |||

$var->threadI(1,\@_); | $var->broadcastI(1,\@_); | |||

} | } | |||

=head2 thread2 | =head2 broadcast2 | |||

=for ref | =for ref | |||

Explicit threading over specified dims using thread id 2. | Explicit broadcasting over specified dims using broadcast id 2. | |||

=for usage | =for usage | |||

$xx = $x->thread2(3,1) | $xx = $x->broadcast2(3,1) | |||

=for example | =for example | |||

Wibble | Wibble | |||

Convenience function interfacing to | Convenience function interfacing to | |||

L<PDL::Slices::threadI|PDL::Slices/threadI>. | L<PDL::Slices::broadcastI|PDL::Slices/broadcastI>. | |||

=cut | =cut | |||

sub PDL::thread2 { | sub PDL::broadcast2 { | |||

my $var = shift; | my $var = shift; | |||

$var->threadI(2,\@_); | $var->broadcastI(2,\@_); | |||

} | } | |||

=head2 thread3 | =head2 broadcast3 | |||

=for ref | =for ref | |||

Explicit threading over specified dims using thread id 3. | Explicit broadcasting over specified dims using broadcast id 3. | |||

=for usage | =for usage | |||

$xx = $x->thread3(3,1) | $xx = $x->broadcast3(3,1) | |||

=for example | =for example | |||

Wibble | Wibble | |||

Convenience function interfacing to | Convenience function interfacing to | |||

L<PDL::Slices::threadI|PDL::Slices/threadI>. | L<PDL::Slices::broadcastI|PDL::Slices/broadcastI>. | |||

=cut | =cut | |||

sub PDL::thread3 { | sub PDL::broadcast3 { | |||

my $var = shift; | my $var = shift; | |||

$var->threadI(3,\@_); | $var->broadcastI(3,\@_); | |||

} | } | |||

my %info = ( | my %info = ( | |||

D => { | D => { | |||

Name => 'Dimension', | Name => 'Dimension', | |||

Sub => \&PDL::Core::dimstr, | Sub => \&PDL::Core::dimstr, | |||

}, | }, | |||

T => { | T => { | |||

Name => 'Type', | Name => 'Type', | |||

Sub => sub { return $_[0]->type->shortctype; }, | Sub => sub { return $_[0]->type->shortctype; }, | |||

skipping to change at line 1794 | skipping to change at line 1817 | |||

$ivdformat =~ s/"//g; | $ivdformat =~ s/"//g; | |||

sprintf "%$ivdformat", $_[0]->address } | sprintf "%$ivdformat", $_[0]->address } | |||

}, | }, | |||

); | ); | |||

# print the dimension information about a pdl in some appropriate form | # print the dimension information about a pdl in some appropriate form | |||

sub dimstr { | sub dimstr { | |||

my $this = shift; | my $this = shift; | |||

my @dims = $this->dims; | my @dims = $this->dims; | |||

my @ids = $this->threadids; | my @ids = $this->broadcastids; | |||

my ($nids,$i) = ($#ids - 1,0); | my ($nids,$i) = ($#ids - 1,0); | |||

my $dstr = 'D ['. join(',',@dims[0..($ids[0]-1)]) .']'; | my $dstr = 'D ['. join(',',@dims[0..($ids[0]-1)]) .']'; | |||

if ($nids > 0) { | if ($nids > 0) { | |||

for $i (1..$nids) { | for $i (1..$nids) { | |||

$dstr .= " T$i [". join(',',@dims[$ids[$i]..$ids[$i+1]-1]) .']'; | $dstr .= " T$i [". join(',',@dims[$ids[$i]..$ids[$i+1]-1]) .']'; | |||

} | } | |||

} | } | |||

return $dstr; | return $dstr; | |||

} | } | |||

skipping to change at line 2803 | skipping to change at line 2826 | |||

=for ref | =for ref | |||

Convert ndarray to perl list | Convert ndarray to perl list | |||

=for usage | =for usage | |||

@tmp = list $x; | @tmp = list $x; | |||

Obviously this is grossly inefficient for the large datasets PDL is designed to | Obviously this is grossly inefficient for the large datasets PDL is designed to | |||

handle. This was provided as a get out while PDL matured. It should now be mostl y | handle. This was provided as a get out while PDL matured. It should now be mostl y | |||

superseded by superior constructs, such as PP/threading. However it is still | superseded by superior constructs, such as PP/broadcasting. However it is still | |||

occasionally useful and is provided for backwards compatibility. | occasionally useful and is provided for backwards compatibility. | |||

=for example | =for example | |||

for (list $x) { | for (list $x) { | |||

# Do something on each value... | # Do something on each value... | |||

} | } | |||

=for bad | =for bad | |||

list converts any bad values into the string 'BAD'. | list converts any bad values into the string 'BAD'. | |||

=cut | =cut | |||

# No threading, just the ordinary dims. | # No broadcasting, just the ordinary dims. | |||

sub PDL::list{ # pdl -> @list | sub PDL::list{ # pdl -> @list | |||

barf 'Usage: list($pdl)' if $#_!=0; | barf 'Usage: list($pdl)' if $#_!=0; | |||

my $pdl = PDL->topdl(shift); | my $pdl = PDL->topdl(shift); | |||

return () if nelem($pdl)==0; | return () if nelem($pdl)==0; | |||

@{listref_c($pdl)}; | @{listref_c($pdl)}; | |||

} | } | |||

=head2 unpdl | =head2 unpdl | |||

=for ref | =for ref | |||

skipping to change at line 2893 | skipping to change at line 2916 | |||

Convert ndarray indices to perl list | Convert ndarray indices to perl list | |||

=for usage | =for usage | |||

@tmp = listindices $x; | @tmp = listindices $x; | |||

C<@tmp> now contains the values C<0..nelem($x)>. | C<@tmp> now contains the values C<0..nelem($x)>. | |||

Obviously this is grossly inefficient for the large datasets PDL is designed to | Obviously this is grossly inefficient for the large datasets PDL is designed to | |||

handle. This was provided as a get out while PDL matured. It should now be most ly | handle. This was provided as a get out while PDL matured. It should now be most ly | |||

superseded by superior constructs, such as PP/threading. However it is still | superseded by superior constructs, such as PP/broadcasting. However it is still | |||

occasionally useful and is provied for backwards compatibility. | occasionally useful and is provied for backwards compatibility. | |||

=for example | =for example | |||

for $i (listindices $x) { | for $i (listindices $x) { | |||

# Do something on each value... | # Do something on each value... | |||

} | } | |||

=cut | =cut | |||

skipping to change at line 3702 | skipping to change at line 3725 | |||

See L<PDL::ParallelCPU> for an overview of the auto-pthread process. | See L<PDL::ParallelCPU> for an overview of the auto-pthread process. | |||

=for example | =for example | |||

# Example turning on auto-pthreading for a target of 2 pthreads and for functi ons involving | # Example turning on auto-pthreading for a target of 2 pthreads and for functi ons involving | |||

# PDLs with greater than 1M elements | # PDLs with greater than 1M elements | |||

set_autopthread_targ(2); | set_autopthread_targ(2); | |||

set_autopthread_size(1); | set_autopthread_size(1); | |||

# Execute a pdl function, processing will split into two pthreads as long as | # Execute a pdl function, processing will split into two pthreads | |||

# one of the pdl-threaded dimensions is divisible by 2. | ||||

$x = minimum($y); | $x = minimum($y); | |||

# Get the actual number of pthreads that were run. | # Get the actual number of pthreads that were run. | |||

$actual_pthread = get_autopthread_actual(); | $actual_pthread = get_autopthread_actual(); | |||

=cut | =cut | |||

*set_autopthread_targ = \&PDL::set_autopthread_targ; | *set_autopthread_targ = \&PDL::set_autopthread_targ; | |||

=head2 get_autopthread_targ | =head2 get_autopthread_targ | |||

End of changes. 75 change blocks. | ||||

79 lines changed or deleted | | 103 lines changed or added |