NAME

PDL::PP - Generate PDL routines from concise descriptions

SYNOPSIS

e.g.

pp_def(
	'sumover',
	Pars => 'a(n); [o]b();',
	Code => 'double tmp=0;
		loop(n) %{ tmp += $a(); %}
		$b() = tmp;
		'
);

pp_done();

(remember to include '-MPDL::PP Module...' in Makefile.PL)

DESCRIPTION

The PDL::PP module provides a way of concisely describing an operation on some data, so that this description may then be compiled into C and object code.

PDL::PP is only of interest if you want to

  • Interface PDL to some external library

  • Write some algorithm that would be slow on the perl side in C (this is not as often as you think; take a look at threading and dataflow first).

  • Be a PDL developer (and even then it's not obligatory)

In order to understand all this, you should be familiar with the other features of PDL, like threading and dataflow.

What happens when you run the code in the SYNOPSIS? The function pp_def takes as arguments first the name of the function you are defining and then a hash list that can contain various keys.

Based on the keys, transformations are applied to a hash, resulting in the end in XS code and a .pm file. The function pp_done takes these (they were stored in a global variable) and writes the actual files (the file names were given when the module was imported).

There may be several pp_def() calls inside a file but generally only one pp_done().

WARNING

Because of its architecture, PDL::PP can be both flexible and easy to use (yet exuberantly complicated) at the same time. The problem is that if something goes wrong, you'd better know what you are doing and be able to hack your way through the internals.

An alternative of course is to ask someone about it.

ABANDON ALL HOPE, YE WHO ENTER HERE (DESCRIPTION)

There are two main syntaxes for the parameters of pp_def(): the 'data operation' and 'slice operation' prototypes.

The 'data operation' is used to take some data, mangle it and give out some other data; examples include the '+' operation, matrix inverse, sumover etc.

The 'slice operation' is a different kind of operation: in a slice operation, you are not changing any data, you are defining correspondences between different elements of two piddles.

If you are just interested in communicating with some external library, you'll usually want the 'data operation' so we are going to discuss that first.

Data operation

In the data operation, you must know what dimensions of data you need. First, an example with scalars:

pp_def('add',
	Pars => 'a(); b(); [o]c();',
	Code => '$c() = $a() + $b();'
);

That looks a little strange but let's dissect it. The first line is self-evident: we're defining a routine with the name 'add'. The second line simply declares our parameters and the parentheses mean that they are scalars.

The third line is the actual operation. You need to use the dollar signs and parentheses to refer to your parameters (this will probably change at some point in the future, once a good syntax is found).

These lines are all that is necessary to actually define the function for PDL so now you can do

use MyModule;
$a = pdl 2,3,4;
$b = pdl 5;
add($a,$b,($c=null));

and have threading work correctly (the result is $c == [7 8 9]).

Implicit and explicit threading and the creation of the result are taken care of automatically. You can even do dataflow with add.

What about dimensions? Let's say that we want to add a scalar + the index number to a vector:

pp_def('add2',
	Pars => 'a(n); b(); [o]c(n);',
	Code => 'loop(n) %{
			$c() = $a() + $b() + n;
		 %}'
);

There are several points to notice here: first, the Pars argument now contains the n arguments to show that we have a single dimensions in a and c. It is important to note that dimensions are actual entities that are accessed by name so this declares a and c to have the same first dimensions.

The loop construct also refers to the dimension name so you don't need to specify any limits: the loop correctly sized and everything is done for you, again.

Next, there is the surprising fact that $a() and $c() do not contain the index. This is not necessary because we're looping over n and both variables know that they have a dimensions 'n' so they automatically know they're being looped over.

This feature comes in very handy in many places and makes for much shorter code. Of course, there are times when you want to circumvent this; here is a function which symmetrizes a matrix:

pp_def('symm',
	Pars => 'a(n,n); [o]c(n,n);',
	Code => 'loop(n) %{
			int n2;
			for(n2=n; n2<n_size; n2++) {
				$c(n0 => n, n1 => n2) =
				$c(n0 => n2, n1 => n) =
				 $a(n0 => n, n1 => n2);
			}
		%}
	'
);

What happens here is probably self-evident.

Now, consider the following: you have your own C function which takes as arguments two pointers to vectors of double:

void myfunc(int n,double *v1,double *v2);

The correct way of defining the PDL function is

pp_def('myfunc',
	Pars => 'a(n); [o]b(n);',
	GenericTypes => [D],
	Code => 'myfunc(n_size,$P(a),$P(b));'
);

The $P(par) syntax returns a pointer to the first element and the other elements are guaranteed to lie after that.

Notice that here it is possible to make many mistakes. First, n_size must be used instead of n. Second, you shouldn't put any loops in this code. Third, the GenericTypes declaration is especially important since otherwise the function might get called with pointers to char, for instance (a bad bad thing).

The more complicated examples will be here later on; contact Tjl if you need them now.

Slice operation

The slice operation section of this manual is provided using dataflow and lazy evaluation: when you need it, ask Tjl to write it. 24h delivery from when I receive the email is 95% probable and 48h delivery is 99% probable.

INTERNALS

The internals of the current version consist of a large table which gives the rules according to which things are translated and the subs which implement these rules.

Later on, it would be good to make the table modifiable by the user so that different things may be tried.

BUGS

PDL::PP is still, even in its rewritten form, too complicated. It needs to be rethought a little as well as deconvoluted and modularized some more (e.g. all the NS things).

After the rewrite, this can happen a little by little, though.

AUTHOR

Copyright(C) 1997 Tuomas J. Lukka (lukka@fas.harvard.edu). Redistribution in the same form is allowed provided that the copyright notice stays intact but reprinting requires a permission from the author.

1 POD Error

The following errors were encountered while parsing the POD:

Around line 119:

Deleting unknown formatting code P<>