Skip to content

How to Learn PDL::PP using Inline::Pdlpp

Pablo Rodríguez González edited this page Nov 15, 2015 · 4 revisions

If you would like to interface your PDL code with routines written in C then you will need to begin working with PDL::PP. PDL::PP allows you to use a syntax very similar to C in which you specify - with the verbosity and precision of C - exactly what you want PDL to do with your data.

TOC

A brief interlude - whence comes PDL::PP?

The PP stands for 'pre-processor' because PDL::PP will take your specifications and generate XS code from it. What is XS? XS is basically the way you interface C-code with Perl. So cutting out the middle part, since it doesn't really enter into our workflow much, PDL::PP is a way to interface PDL with C-code.

For more on XS, see http://perldoc.perl.org/perlxs.html. For more about the guts of PDL::PP, read on, or check out the docs page here: http://pdl.sourceforge.net/PDLdocs/PP.html....

Using Inline::Pdlpp

While PDL::PP is great, this cookbook shows that it's not trivial to get plain ol' PDL::PP code set up. This sort of arrangement makes sense if you want to package an interface to a C or FORTRAN library, but if you simply want to write some customized code in C, it seems like quite a barrier.

Enter Inline, and Inline::Pdlpp. You can read more about both of those on the web, but I will get down to some code to illustrate how it works. (Note: you'll need to get Inline from the CPAN for these scripts to run, but I will assure you it is definitely worth it.)

A Trivial Example

#!/usr/bin/perl 
use PDL; 
use Inline qw( Pdlpp ); 

my $seq1 = sequence(4) + 1; my $seq2 = 2 + 3 * sequence(4);
print "Original sequences: ", $seq1, $seq2, "\n";
my ($sum, $mult) = accum($seq1, $seq2);
print "Sum: $sum; Multiplicative accumulation: $mult\n";

=head3 accum
 Here's a little bit of syntactic sugar to make calling the function a bit 
 more natural; otherwise we'd have to call the accumulator as 
 $seq1->accum($seq2) 
 which seems awkward. 
=cut

sub accum { 
    my $piddle = shift;
    $piddle->accum(@_);
} 

__DATA__

__Pdlpp__

pp_def('accum', 

       Pars => 'a(n); b(n); [o] sum(); [o] mult();',
       Code =>
           '
               $sum() = 0;
               $mult() = 1;
               loop(n) %{
                   $sum() += $a() + $b();
                   $mult() *= $a() * $b();
               %}
           ',


); 

An almost useless example, yes, but it demonstrates many things for us.

  1. To declare a Pdlpp function, you have to call

    pp_def(, )

The first argument is the name of the function and the remaining arguments are key/value pairs of a hash, the most important of which are Pars and Code. 2. The function parameters, i.e. the variables that would normally go into the function declaration, are designated in the Pars hash entry, whose corresponding value is a string. The string contains semicolon-separated variable specifications as

    (<dimension>); <var-name>(<dimension>); ...

I've not used the dimension spec here; we'll get to that shortly. Output variables are preceded by [o], as in

    [o] <output-var-name>(<dimension>); ... 

and do not necessarily have to come at the end of the parameter specification. 3. In order to access function parameters in the code itself, we use the syntax

$()
The parentheses at this point may seem unnecessary, but they clarify the code under certain circumstances (se PDL::PP for details). 4. We only told PDL::PP what to do with a single input, but we were able to call the function on an array and it Does What I Mean. This is threading in action and helps to vastly simplify our code.

A Less Trivial Example

Now we'll look at something almost as trivial: an accumulator that operates on two piddles and returns two results:

#!/usr/bin/perl 
use PDL; 
use Inline qw( Pdlpp ); 

my $seq1 = sequence(4) + 1; my $seq2 = 2 + 3 * sequence(4);
print "Original sequences: ", $seq1, $seq2, "\n";
my ($sum, $mult) = accum($seq1, $seq2);
print "Sum: $sum; Multiplicative accumulation: $mult\n";

=head3 accum
 Here's a little bit of syntactic sugar to make calling the function a bit 
 more natural; otherwise we'd have to call the accumulator as 
 $seq1->accum($seq2) 
 which seems awkward. 
=cut

sub accum { 
    my $piddle = shift;
    $piddle->accum(@_);
} 

__DATA__ 

__Pdlpp__ 

pp_def('accum', 
    
       Pars => 'a(n); b(n); [o] sum(); [o] mult();',
       Code =>
           '
               $sum() = 0;
               $mult() = 1;
               loop(n) %{
                   $sum() += $a() + $b();
                   $mult() *= $a() * $b();
               %}
           ',
    

); 

What's changed between this code and the last? Note the following:

  1. We have multiple inputs, a and b. If we wanted to include even more, we would tac them onto the list.
  2. We have multiple outputs. They are returned in a Perl array, just as multiple outputs in standard Perl are returned in a Perl array.
  3. The inputs must be at least one-dimensional, and their dimension place-holder is specified in the parentheses in the Pars section. Notice that a and b have the same size for their dimension, so the place-holder (n) is the same.
  4. To create a loop over a dimension, we use the loop construction:
    loop() %{  %} Notice we don't have to reference the placeholder when accessing that element of a or b when working within the loop construction.

Both Inline::Pdlpp and PDL::PP have many more capabilities that I have not touched upon here. Hopefully now you have enough of a foot-hold to read through the PDL::PP documentation, and enough of a working template to experiment as you go along.

Good luck and have fun!

Clone this wiki locally