~ubuntu-branches/ubuntu/lucid/pdl/lucid

« back to all changes in this revision

Viewing changes to Basic/Primitive/primitive.pd

  • Committer: Bazaar Package Importer
  • Author(s): Ben Gertzfield
  • Date: 2002-04-08 18:47:16 UTC
  • Revision ID: james.westby@ubuntu.com-20020408184716-0hf64dc96kin3htp
Tags: upstream-2.3.2
ImportĀ upstreamĀ versionĀ 2.3.2

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
use strict;
 
2
 
 
3
# check for bad value support
 
4
use PDL::Config;
 
5
my $bvalflag = $PDL::Config{WITH_BADVAL} || 0;
 
6
 
 
7
pp_addhdr(<<'EOD');
 
8
 
 
9
#ifndef RAND_MAX
 
10
#error "You must have a working RAND_MAX! Something's wrong with your include files"
 
11
#endif
 
12
 
 
13
EOD
 
14
 
 
15
pp_addpm({At=>'Top'},<<'EOD');
 
16
 
 
17
use PDL::Slices;
 
18
use Carp;
 
19
 
 
20
=head1 NAME
 
21
 
 
22
PDL::Primitive - primitive operations for pdl
 
23
 
 
24
=head1 DESCRIPTION
 
25
 
 
26
This module provides some primitive and useful functions defined
 
27
using PDL::PP and able to use the new indexing tricks.
 
28
 
 
29
See L<PDL::Indexing|PDL::Indexing> for how to use indices creatively.
 
30
For explanation of the signature format, see L<PDL::PP|PDL::PP>.
 
31
 
 
32
=head1 SYNOPSIS
 
33
 
 
34
 use PDL::Primitive;
 
35
 
 
36
=cut
 
37
 
 
38
EOD
 
39
 
 
40
pp_addpm({At=>'Bot'},<<'EOD');
 
41
 
 
42
=head1 AUTHOR
 
43
 
 
44
Copyright (C) Tuomas J. Lukka 1997 (lukka@husc.harvard.edu). Contributions
 
45
by Christian Soeller (c.soeller@auckland.ac.nz) and Karl Glazebrook
 
46
(kgb@aaoepp.aao.gov.au).
 
47
All rights reserved. There is no warranty. You are allowed
 
48
to redistribute this software / documentation under certain
 
49
conditions. For details, see the file COPYING in the PDL
 
50
distribution. If this file is separated from the PDL distribution,
 
51
the copyright notice should be included in the file.
 
52
 
 
53
=cut
 
54
 
 
55
EOD
 
56
 
 
57
 
 
58
################################################################
 
59
#  a whole bunch of quite basic functions for inner, outer
 
60
#  and matrix products (operations that are not normally
 
61
#  available via operator overloading)
 
62
################################################################
 
63
 
 
64
pp_def(
 
65
       'inner',
 
66
       HandleBad => 1,
 
67
       Pars => 'a(n); b(n); [o]c();', 
 
68
       Code => 
 
69
       'double tmp = 0;
 
70
        loop(n) %{ tmp += $a() * $b(); %}
 
71
        $c() = tmp;',
 
72
       BadCode => 
 
73
       'double tmp = 0;
 
74
        int flag = 0;
 
75
        loop(n) %{ 
 
76
           if ( $ISGOOD(a()) && $ISGOOD(b()) ) { tmp += $a() * $b(); flag = 1; }
 
77
        %}
 
78
        if ( flag ) { $c() = tmp; }
 
79
        else        { $SETBAD(c()); $PDLSTATESETBAD(c); }',
 
80
       CopyBadStatusCode => '',
 
81
       Doc => '
 
82
=for ref
 
83
 
 
84
Inner product over one dimension
 
85
 
 
86
 c = sum_i a_i * b_i
 
87
 
 
88
',
 
89
       BadDoc => 
 
90
'If C<a() * b()> contains only bad data,
 
91
C<c()> is set bad. Otherwise C<c()> will have its bad flag cleared,
 
92
as it will not contain any bad values.',
 
93
       ); # pp_def( inner )
 
94
 
 
95
 
 
96
pp_def(
 
97
       'outer',
 
98
       HandleBad => 1,
 
99
       Pars => 'a(n); b(m); [o]c(n,m);',
 
100
       Code => 
 
101
       'loop(n,m) %{ 
 
102
          $c() = $a() * $b(); 
 
103
        %}',
 
104
       BadCode => 
 
105
       'loop(n,m) %{ 
 
106
          if ( $ISBAD(a()) || $ISBAD(b()) ) {
 
107
             $SETBAD(c());
 
108
          } else {
 
109
             $c() = $a() * $b(); 
 
110
          }
 
111
        %}',
 
112
       Doc => '
 
113
=for ref
 
114
 
 
115
outer product over one dimension
 
116
 
 
117
Naturally, it is possible to achieve the effects of outer
 
118
product simply by threading over the "C<*>"
 
119
operator but this function is provided for convenience.
 
120
 
 
121
'); # pp_def( outer )
 
122
 
 
123
 
 
124
pp_addpm(<<'EOD');
 
125
=head2 matmult
 
126
 
 
127
=for sig
 
128
 
 
129
 Signature: (a(x,y),b(y,z),[o]c(x,z))
 
130
 
 
131
=for ref
 
132
 
 
133
Matrix multiplication
 
134
 
 
135
We peruse the inner product to define matrix multiplication
 
136
via a threaded inner product
 
137
 
 
138
=cut
 
139
 
 
140
  sub PDL::matmult {
 
141
    barf "Invalid number of arguments for matmult" if $#_ < 1;
 
142
    my ($a,$b,$c) = @_;
 
143
    while ($a->getndims < 2) {$a = $a->dummy(-1)} # promote if necessary
 
144
    while ($b->getndims < 2) {$b = $b->dummy(-1)}
 
145
    if(!defined $c) {$c = PDL->nullcreate($a)}
 
146
    $a->dummy(1)->inner($b->xchg(0,1)->dummy(2),$c);
 
147
    return $c;
 
148
  }
 
149
 
 
150
  *matmult = \&PDL::matmult;
 
151
 
 
152
EOD
 
153
 
 
154
pp_add_exported('', 'matmult');
 
155
 
 
156
pp_def(
 
157
       'innerwt',
 
158
       HandleBad => 1,
 
159
       Pars => 'a(n); b(n); c(n); [o]d();',
 
160
       Code => 
 
161
       'double tmp = 0;
 
162
        loop(n) %{ 
 
163
           tmp += $a() * $b() * $c(); 
 
164
        %}
 
165
        $d() = tmp;',
 
166
       BadCode => 
 
167
       'double tmp = 0;
 
168
        int flag = 0;
 
169
 
 
170
        loop(n) %{ 
 
171
           if ( $ISGOOD(a()) && $ISGOOD(b()) && $ISGOOD(c()) ) { 
 
172
              tmp += $a() * $b() * $c(); 
 
173
              flag = 1;
 
174
           }
 
175
        %}
 
176
        if ( flag ) { $d() = tmp; }
 
177
        else        { $SETBAD(d()); }',
 
178
       Doc => 
 
179
'=for ref
 
180
 
 
181
Weighted (i.e. triple) inner product
 
182
 
 
183
 d = sum_i a(i) b(i) c(i)
 
184
 
 
185
'
 
186
       );
 
187
 
 
188
pp_def(
 
189
       'inner2',
 
190
       HandleBad => 1,
 
191
       Pars => 'a(n); b(n,m); c(m); [o]d();',
 
192
       Code => 
 
193
       'double tmp=0;
 
194
        loop(n,m) %{
 
195
           tmp += $a() * $b() * $c();
 
196
        %}
 
197
        $d() = tmp;',
 
198
       BadCode => 
 
199
       'double tmp = 0;
 
200
        int flag = 0;
 
201
        loop(n,m) %{ 
 
202
           if ( $ISGOOD(a()) && $ISGOOD(b()) && $ISGOOD(c()) ) { 
 
203
              tmp += $a() * $b() * $c();
 
204
              flag = 1;
 
205
           }
 
206
        %}
 
207
        if ( flag ) { $d() = tmp; }
 
208
        else        { $SETBAD(d()); }',
 
209
       Doc =>
 
210
'=for ref
 
211
 
 
212
Inner product of two vectors and a matrix
 
213
 
 
214
 d = sum_ij a(i) b(i,j) c(j)
 
215
 
 
216
Note that you should probably not thread over C<a> and C<c> since that would be  
 
217
very wasteful. Instead, you should use a temporary for C<b*c>.
 
218
 
 
219
'
 
220
       );
 
221
 
 
222
 
 
223
pp_def(
 
224
       'inner2d',
 
225
       HandleBad => 1,
 
226
       Pars => 'a(n,m); b(n,m); [o]c();',
 
227
       Code => 
 
228
       'double tmp=0;
 
229
        loop(n,m) %{
 
230
           tmp += $a() * $b();
 
231
        %}
 
232
        $c() = tmp;',
 
233
       BadCode => 
 
234
       'double tmp = 0;
 
235
        int flag = 0;
 
236
        loop(n,m) %{ 
 
237
           if ( $ISGOOD(a()) && $ISGOOD(b()) ) { 
 
238
              tmp += $a() * $b();
 
239
              flag = 1;
 
240
           }
 
241
        %}
 
242
        if ( flag ) { $c() = tmp; }
 
243
        else        { $SETBAD(c()); }',
 
244
       Doc =>
 
245
'=for ref
 
246
 
 
247
Inner product over 2 dimensions.
 
248
 
 
249
Equivalent to
 
250
 
 
251
 $c = inner($a->clump(2), $b->clump(2))
 
252
 
 
253
'
 
254
       );
 
255
 
 
256
pp_def(
 
257
       'inner2t',
 
258
       HandleBad => 1,
 
259
       Pars => 'a(j,n); b(n,m); c(m,k); [t]tmp(n,k); [o]d(j,k));',
 
260
       Code => 
 
261
       'loop(n,k) %{ 
 
262
           double tmp0 = 0;
 
263
           loop(m) %{ 
 
264
              tmp0 += $b() * $c(); 
 
265
           %}
 
266
           $tmp() = tmp0;
 
267
        %}
 
268
        loop(j,k) %{ 
 
269
           double tmp1 = 0;
 
270
           loop(n) %{ 
 
271
              tmp1 += $a() * $tmp(); 
 
272
           %}
 
273
           $d() = tmp1;
 
274
        %}',
 
275
       BadCode => 
 
276
       'loop(n,k) %{ 
 
277
           double tmp0 = 0;
 
278
           int flag = 0;
 
279
           loop(m) %{ 
 
280
              if ( $ISGOOD(b()) && $ISGOOD(c()) ) {
 
281
                 tmp0 += $b() * $c(); 
 
282
                 flag = 1;
 
283
              }
 
284
           %}
 
285
           if ( flag ) { $tmp() = tmp0; }
 
286
           else        { $SETBAD(tmp()); }
 
287
        %}
 
288
        loop(j,k) %{ 
 
289
           double tmp1 = 0;
 
290
           int flag = 0;
 
291
           loop(n) %{ 
 
292
              if ( $ISGOOD(a()) && $ISGOOD(tmp()) ) {
 
293
                 tmp1 += $a() * $tmp(); 
 
294
                 flag = 1;
 
295
              }
 
296
           %}
 
297
           if ( flag ) { $d() = tmp1; }
 
298
           else        { $SETBAD(d()); }
 
299
        %}',
 
300
       Doc =>
 
301
'=for ref
 
302
 
 
303
Efficient Triple matrix product C<a*b*c>
 
304
 
 
305
Efficiency comes from by using the temporary C<tmp>. This operation only 
 
306
scales as C<N**3> whereas threading using L<inner2|/inner2> would scale 
 
307
as C<N**4>.
 
308
 
 
309
The reason for having this routine is that you do not need to
 
310
have the same thread-dimensions for C<tmp> as for the other arguments,
 
311
which in case of large numbers of matrices makes this much more
 
312
memory-efficient.
 
313
 
 
314
It is hoped that things like this could be taken care of as a kind of
 
315
closures at some point.
 
316
 
 
317
'
 
318
       ); # pp_def inner2t()
 
319
 
 
320
 
 
321
# a helper function for the cross product definition
 
322
sub crassgn {
 
323
  "\$c(tri => $_[0]) = \$a(tri => $_[1])*\$b(tri => $_[2]) -
 
324
        \$a(tri => $_[2])*\$b(tri => $_[1]);"
 
325
}
 
326
 
 
327
pp_def('crossp',
 
328
       Doc => <<'EOD',
 
329
=for ref
 
330
 
 
331
Cross product of two 3D vectors
 
332
 
 
333
After
 
334
 
 
335
=for example
 
336
 
 
337
 $c = crossp $a, $b
 
338
 
 
339
the inner product C<$c*$a> and C<$c*$b> will be zero, i.e. C<$c> is
 
340
orthogonal to C<$a> and C<$b>
 
341
 
 
342
=cut
 
343
EOD
 
344
       Pars => 'a(tri=3); b(tri); [o] c(tri)',
 
345
       Code => 
 
346
       crassgn(0,1,2)."\n". 
 
347
       crassgn(1,2,0)."\n".
 
348
       crassgn(2,0,1),
 
349
       );
 
350
 
 
351
pp_def('norm',
 
352
       HandleBad => 1,
 
353
       Pars => 'vec(n); [o] norm(n)',
 
354
       Doc => 'Normalises a vector to unit Euclidean length',
 
355
       Code => 
 
356
       'double sum=0;
 
357
        loop(n) %{ sum += $vec()*$vec(); %}
 
358
        if (sum > 0) {
 
359
          sum = sqrt(sum);
 
360
          loop(n) %{ $norm() = $vec()/sum; %}
 
361
        } else {
 
362
          loop(n) %{ $norm() = $vec(); %}
 
363
        }',
 
364
       BadCode => 
 
365
       'double sum=0;
 
366
        int flag = 0;
 
367
        loop(n) %{ 
 
368
           sum += $vec()*$vec(); 
 
369
           flag = 1;
 
370
        %}
 
371
        if ( flag ) {
 
372
           if (sum > 0) {
 
373
              sum = sqrt(sum);
 
374
              loop(n) %{ 
 
375
                 if ( $ISBAD(vec()) ) { $SETBAD(norm()); }
 
376
                 else                 { $norm() = $vec()/sum; }
 
377
              %}
 
378
           } else {
 
379
              loop(n) %{ 
 
380
                 if ( $ISBAD(vec()) ) { $SETBAD(norm()); }
 
381
                 else                 { $norm() = $vec(); }
 
382
              %}
 
383
           }
 
384
        } else {
 
385
           loop(n) %{ 
 
386
              $SETBAD(norm());
 
387
           %}
 
388
        }',
 
389
);
 
390
 
 
391
# this one was motivated by the need to compute
 
392
# the circular mean efficiently
 
393
# without it could not be done efficiently or without
 
394
# creating large intermediates (check pdl-porters for
 
395
# discussion)
 
396
# see PDL::ImageND for info about the circ_mean function
 
397
pp_def(
 
398
    'indadd',
 
399
    HandleBad => 1,
 
400
    Pars => 'a(); int ind(); [o] sum(m)',
 
401
    Code => 
 
402
    'register int foo = $ind();
 
403
     if( foo<0 || foo>=$SIZE(m) ) {
 
404
       barf("PDL::indadd: invalid index");
 
405
     }
 
406
     $sum(m => foo) += $a();',
 
407
    BadCode => 
 
408
    'register int foo = $ind();
 
409
     if( $ISBADVAR(foo,ind) || foo<0 || foo>=$SIZE(m) ) {
 
410
       barf("PDL::indadd: invalid index");
 
411
     }
 
412
     if ( $ISBAD(a()) ) { $SETBAD(sum(m => foo)); }
 
413
     else               { $sum(m => foo) += $a(); }',
 
414
    BadDoc =>
 
415
'The routine barfs if any of the indices are bad.',
 
416
    Doc=>'
 
417
=for ref
 
418
 
 
419
Threaded Index Add: Add C<a> to the C<ind> element of C<sum>, i.e:
 
420
 
 
421
 sum(ind) += a
 
422
 
 
423
=for example
 
424
 
 
425
Simple Example:
 
426
 
 
427
  $a = 2;
 
428
  $ind = 3;
 
429
  $sum = zeroes(10);
 
430
  indadd($a,$ind, $sum);
 
431
  print $sum
 
432
  #Result: ( 2 added to element 3 of $sum)
 
433
  # [0 0 0 2 0 0 0 0 0 0]
 
434
 
 
435
Threaded Example:
 
436
 
 
437
  $a = pdl( 1,2,3);
 
438
  $ind = pdl( 1,4,6);
 
439
  $sum = zeroes(10);
 
440
  indadd($a,$ind, $sum);
 
441
  print $sum."\n";
 
442
  #Result: ( 1, 2, and 3 added to elements 1,4,6 $sum)
 
443
  # [0 1 0 0 2 0 3 0 0 0]
 
444
 
 
445
=cut
 
446
');
 
447
 
 
448
# 1D convolution
 
449
# useful for threaded 1D filters
 
450
pp_addhdr('
 
451
/* Fast Modulus with proper negative behaviour */
 
452
#define REALMOD(a,b) while ((a)>=(b)) (a) -= (b); while ((a)<0) (a) += (b); 
 
453
');
 
454
pp_def('conv1d',
 
455
       Doc => << 'EOD',
 
456
=for ref
 
457
 
 
458
1d convolution along first dimension
 
459
 
 
460
=for example
 
461
 
 
462
  $con = conv1d sequence(10), pdl(-1,0,1), {Boundary => 'reflect'};
 
463
 
 
464
By default, periodic boundary conditions are assumed (i.e. wrap around).
 
465
Alternatively, you can request reflective boundary conditions using
 
466
the C<Boundary> option:
 
467
 
 
468
  {Boundary => 'reflect'} # case in 'reflect' doesn't matter
 
469
 
 
470
The convolution is performed along the first dimension. To apply it across
 
471
another dimension use the slicing routines, e.g.
 
472
 
 
473
  $b = $a->mv(2,0)->conv1d($kernel)->mv(0,2); # along third dim
 
474
 
 
475
This function is useful for threaded filtering of 1D signals.
 
476
 
 
477
Compare also L<conv2d|PDL::Image2D/conv2d>, L<convolve|PDL::ImageND/convolve>,
 
478
L<fftconvolve|PDL::FFT/fftconvolve>, L<fftwconv|PDL::FFTW/fftwconv>,
 
479
L<rfftwconv|PDL::FFTW/rfftwconv>
 
480
 
 
481
EOD
 
482
        Pars => 'a(m); kern(p); [o]b(m);',
 
483
        OtherPars => 'int reflect;',
 
484
        PMCode => '
 
485
        
 
486
sub PDL::conv1d {
 
487
   my $opt = pop @_ if ref($_[$#_]) eq \'HASH\';
 
488
   die \'Usage: conv1d( a(m), kern(p), [o]b(m), {Options} )\'
 
489
      if $#_<1 || $#_>2;
 
490
   my($a,$kern) = @_;
 
491
   my $c = $#_ == 2 ? $_[2] : PDL->null;
 
492
   &PDL::_conv1d_int($a,$kern,$c,
 
493
                     !(defined $opt && exists $$opt{Boundary}) ? 0 :
 
494
                     lc $$opt{Boundary} eq "reflect");
 
495
   return $c;
 
496
}
 
497
 
 
498
',              
 
499
        Code => '
 
500
           int i,i1,i2,poff;
 
501
           double tmp;
 
502
           int reflect = $COMP(reflect); 
 
503
           int m_size = $COMP(__m_size); 
 
504
           int p_size = $COMP(__p_size); 
 
505
 
 
506
           poff = (p_size-1)/2;
 
507
           for(i=0; i<m_size; i++) { 
 
508
              tmp = 0; 
 
509
                  for(i1=0; i1<p_size; i1++) { 
 
510
                     i2 = i+i1 - poff; 
 
511
                     if (reflect && i2<0)
 
512
                        i2 = -i2;
 
513
                     if (reflect && i2>=m_size)
 
514
                        i2 = m_size-(i2-m_size+1);
 
515
 
 
516
                     REALMOD(i2,m_size); 
 
517
                     tmp += $a(m=>i2) * $kern(p=>i1);
 
518
                  }
 
519
              $b(m=>i) = tmp;
 
520
           }
 
521
');
 
522
 
 
523
 
 
524
# this can be achieved by
 
525
#  ($a->dummy(0) == $b)->orover
 
526
# but this one avoids a larger intermediate and potentially shortcuts
 
527
pp_def('in',
 
528
        Pars => 'a(); b(n); [o] c()',
 
529
        Code => '$c() = 0;
 
530
                 loop(n) %{ if ($a() == $b()) {$c() = 1; break;} %}',
 
531
        Doc => <<'EOD',
 
532
=for ref
 
533
 
 
534
test if a is in the set of values b
 
535
 
 
536
=for example
 
537
 
 
538
   $goodmsk = $labels->in($goodlabels);
 
539
   print pdl(4,3,1)->in(pdl(2,3,3));
 
540
  [0 1 0]
 
541
 
 
542
C<in> is akin to the I<is an element of> of set theory. In priciple,
 
543
PDL threading could be used to achieve its functionality by using a
 
544
construct like
 
545
 
 
546
   $msk = ($labels->dummy(0) == $goodlabels)->orover;
 
547
 
 
548
However, C<in> doesn't create a (potentially large) intermediate
 
549
and is generally faster.
 
550
EOD
 
551
);
 
552
 
 
553
 
 
554
pp_add_exported '', 'uniq';
 
555
pp_addpm << 'EOPM';
 
556
 
 
557
=head2 uniq
 
558
 
 
559
=for ref
 
560
 
 
561
return all unique elements of a piddle
 
562
 
 
563
The unique elements are returned in ascending order.
 
564
 
 
565
=for example
 
566
 
 
567
  print pdl(2,2,2,4,0,-1,6,6)->uniq;
 
568
 [-1 0 2 4 6]
 
569
 
 
570
Note: The returned pdl is 1D; any structure of the input
 
571
piddle is lost.
 
572
 
 
573
=cut
 
574
 
 
575
*uniq = \&PDL::uniq;
 
576
# return unique elements of array
 
577
# find as jumps in the sorted array
 
578
# flattens in the process
 
579
sub PDL::uniq {
 
580
  use PDL::Core 'barf';
 
581
  my ($arr) = @_;
 
582
  barf "need at least one element" if $arr->nelem == 0;
 
583
  my $srt = $arr->clump(-1)->qsort;
 
584
  my $uniq = $srt->where($srt != $srt->rotate(-1));
 
585
  # make sure we return something if there is only one value
 
586
  return $uniq->nelem == 0 ? $arr->pdl($arr->type,[$arr->clump(-1)->at(0)]) :
 
587
        $uniq;
 
588
}
 
589
 
 
590
EOPM
 
591
 
 
592
#####################################################################
 
593
#  clipping routines
 
594
#####################################################################
 
595
 
 
596
# clipping
 
597
 
 
598
for my $opt (
 
599
             ['hclip','>'],
 
600
             ['lclip','<']
 
601
             ) {
 
602
    my $name = $opt->[0];
 
603
    my $op   = $opt->[1];
 
604
    pp_def(
 
605
           $name,
 
606
           HandleBad => 1,
 
607
           Pars => 'a(); b(); [o] c()',
 
608
           Code => 
 
609
           '$c() = ($a() '.$op.' $b()) ? $b() : $a();',
 
610
           BadCode =>
 
611
           'if ( $ISBAD(a()) || $ISBAD(b()) ) {
 
612
               $SETBAD(c());
 
613
            } else {
 
614
               $c() = ($a() '.$op.' $b()) ? $b() : $a();
 
615
            }',
 
616
           Doc =>  'clip C<$a> by C<$b> (C<$b> is '.
 
617
           ($name eq 'hclip' ? 'upper' : 'lower').' bound)',
 
618
          PMCode=><<"EOD",
 
619
sub PDL::$name {
 
620
   my (\$a,\$b) = \@_;
 
621
   my \$c;
 
622
   if (\$a->is_inplace) {
 
623
       \$a->set_inplace(0); \$c = \$a;
 
624
   } elsif (\$#_ > 1) {\$c=\$_[2]} else {\$c=PDL->nullcreate(\$a)}
 
625
   &PDL::_${name}_int(\$a,\$b,\$c);
 
626
   return \$c;
 
627
}
 
628
EOD
 
629
    ); # pp_def $name
 
630
 
 
631
} # for: my $opt
 
632
 
 
633
pp_add_exported('', 'clip');
 
634
 
 
635
pp_addpm(<<'EOD');
 
636
 
 
637
=head2 clip
 
638
 
 
639
=for ref
 
640
 
 
641
Clip a piddle by (optional) upper or lower bounds.
 
642
 
 
643
=for usage
 
644
 
 
645
 $b = $a->clip(0,3);
 
646
 $c = $a->clip(undef, $x);
 
647
 
 
648
EOD
 
649
 
 
650
    if ( $bvalflag ) {
 
651
        pp_addpm(<<'EOD');
 
652
=for bad
 
653
 
 
654
clip handles bad values since it is just a
 
655
wrapper around L<hclip|/hclip> and
 
656
L<lclip|/lclip>.
 
657
 
 
658
EOD
 
659
} # if: $bvalflag
 
660
 
 
661
pp_addpm(<<'EOD');
 
662
=cut
 
663
 
 
664
*clip = \&PDL::clip;
 
665
sub PDL::clip {
 
666
  my($a, $b, $c) = @_;
 
667
  my $d; if($a->is_inplace) {$a->set_inplace(0); $d = $a} 
 
668
  elsif($#_ > 2) {$d=$_[3]} else {$d = PDL->nullcreate($a)}
 
669
 if(defined $b) {
 
670
        &PDL::_lclip_int($a,$b,$d);
 
671
        if(defined $c) {
 
672
                &PDL::_hclip_int($d,$c,$d);
 
673
        }
 
674
  } elsif(defined $c) {
 
675
        &PDL::_hclip_int($a,$c,$d);
 
676
  }
 
677
  return $d;
 
678
}
 
679
 
 
680
EOD
 
681
 
 
682
############################################################
 
683
# elementary statistics and histograms
 
684
############################################################
 
685
 
 
686
pp_def(
 
687
       'wtstat',
 
688
       HandleBad => 1,
 
689
       Pars => 'a(n); wt(n); avg(); [o]b();',
 
690
       OtherPars => 'int deg',
 
691
       Code =>
 
692
       'double wtsum = 0;
 
693
        double statsum = 0;
 
694
        loop(n) %{
 
695
           register double tmp; 
 
696
           register int i;
 
697
           wtsum += $wt();
 
698
           tmp=1; 
 
699
           for(i=0; i<$COMP(deg); i++) 
 
700
              tmp *= $a();
 
701
           statsum += $wt() * (tmp - $avg()); 
 
702
        %}
 
703
        $b() = statsum / wtsum;',
 
704
       BadCode =>
 
705
       'double wtsum = 0;
 
706
        double statsum = 0;
 
707
        int flag = 0;
 
708
        loop(n) %{
 
709
           if ( $ISGOOD(wt()) && $ISGOOD(a()) && $ISGOOD(avg()) ) {
 
710
              register double tmp; 
 
711
              register int i;
 
712
              wtsum += $wt();
 
713
              tmp=1; 
 
714
              for(i=0; i<$COMP(deg); i++) 
 
715
                 tmp *= $a();
 
716
              statsum += $wt() * (tmp - $avg()); 
 
717
              flag = 1;
 
718
           }
 
719
        %}
 
720
        if ( flag ) { $b() = statsum / wtsum; }
 
721
        else        { $SETBAD(b()); $PDLSTATESETBAD(b); }',
 
722
       CopyBadStatusCode => '',
 
723
       Doc => 
 
724
'=for ref
 
725
 
 
726
Weighted statistical moment of given degree
 
727
 
 
728
This calculates a weighted statistic over the vector C<a>.
 
729
The formula is
 
730
 
 
731
 b() = (sum_i wt_i * (a_i ** degree - avg)) / (sum_i wt_i)
 
732
 
 
733
',
 
734
       BadDoc => 
 
735
'Bad values are ignored in any calculation; C<$b> will only
 
736
have its bad flag set if the output contains any bad data.',
 
737
       );
 
738
 
 
739
 
 
740
 
 
741
pp_def('statsover',
 
742
        HandleBad => 1,
 
743
        Pars => 'a(n); w(n); int+ [o]avg(); int+ [o]rms(); int+ [o]min(); int+ [o]max(); int+ [o]adev()',
 
744
        Code => 
 
745
        '$GENERIC(avg) tmp = 0;
 
746
         $GENERIC(rms) tmp1 = 0;         
 
747
         $GENERIC(adev) tmp2 = 0;
 
748
         $GENERIC(rms) diff = 0;
 
749
         $GENERIC(min) curmin, curmax;
 
750
         $GENERIC(w) norm = 0;
 
751
         loop(n) %{ 
 
752
            tmp += $a()*$w();
 
753
            norm += $w();
 
754
            if (!n) { curmin = $a(); curmax = $a();}
 
755
            if ($a() < curmin) { 
 
756
                curmin = $a(); 
 
757
             } else if ($a() > curmax) {
 
758
                curmax = $a();
 
759
             } 
 
760
         %}
 
761
         $avg() = tmp / norm;
 
762
         $min() = curmin;
 
763
         $max() = curmax;
 
764
         /* Calculate the RMS using the corrected two-pass algorithm */
 
765
         tmp = 0; 
 
766
         loop(n) %{
 
767
            diff = ($a()*$w() - $avg());
 
768
            tmp += diff*diff;
 
769
            tmp1 += diff;
 
770
            tmp2 += abs(diff);
 
771
         %}
 
772
         $rms() = sqrt ( (tmp - tmp1*tmp1/norm)/((norm - ($GENERIC(rms)) 1)) );
 
773
         $adev() = sqrt( tmp2/ norm );',
 
774
        BadCode => 
 
775
        '$GENERIC(avg) tmp = 0;
 
776
         $GENERIC(rms) tmp1 = 0;         
 
777
         $GENERIC(adev) tmp2 = 0;
 
778
         $GENERIC(rms) diff = 0;
 
779
         $GENERIC(min) curmin, curmax;
 
780
         $GENERIC(w) norm = 0;
 
781
         int flag = 0;
 
782
         loop(n) %{
 
783
             /* perhaps should check w() for bad values too ? */
 
784
             if ( $ISGOOD(a()) ) {
 
785
              tmp += $a()*$w();
 
786
              norm += $w();
 
787
              if (!flag) { curmin = $a(); curmax = $a(); flag=1; }               
 
788
              if ($a() < curmin) { 
 
789
                curmin = $a(); 
 
790
              } else if ($a() > curmax) {
 
791
                curmax = $a();
 
792
              }
 
793
            } 
 
794
         %}
 
795
         /* have at least one valid point if flag == 1 */
 
796
         if ( flag ) { 
 
797
           $avg() = tmp / norm;
 
798
           $min() = curmin;
 
799
           $max() = curmax;
 
800
           tmp = 0; 
 
801
           loop(n) %{
 
802
              if ($ISGOOD(a())) { 
 
803
                 diff = ($a()*$w() - $avg());
 
804
                 tmp += diff*diff;
 
805
                 tmp1 += diff;
 
806
                 tmp2 += abs(diff);
 
807
              }
 
808
           %}
 
809
           $rms() = sqrt ( (tmp - tmp1*tmp1/norm)/( (norm - ($GENERIC(rms)) 1)));
 
810
           $adev() = sqrt ( tmp2 / norm);
 
811
         } else       { 
 
812
           $SETBAD(avg()); 
 
813
           $SETBAD(rms());
 
814
           $SETBAD(adev());
 
815
           $SETBAD(min());
 
816
           $SETBAD(max());
 
817
         }',
 
818
      PMCode => '
 
819
 
 
820
sub PDL::statsover {
 
821
   barf(\'Usage: ($mean,[$rms, $median, $min, $max, $adev]) = statsover($data,[$weights])\') if $#_>1;
 
822
   my ($data, $weights) = @_;
 
823
   $weights = $data->ones() if !defined($weights);
 
824
   
 
825
   my $median = $data->medover();
 
826
   my $mean = PDL->nullcreate($data);
 
827
   my $rms = PDL->nullcreate($data);
 
828
   my $min = PDL->nullcreate($data);
 
829
   my $max = PDL->nullcreate($data);
 
830
   my $adev = PDL->nullcreate($data);
 
831
   &PDL::_statsover_int($data, $weights, $mean, $rms, $min, $max, $adev);
 
832
 
 
833
   return $mean unless wantarray;
 
834
   return ($mean, $rms, $median, $min, $max, $adev);
 
835
}
 
836
 
 
837
',
 
838
      Doc => 
 
839
'
 
840
=for ref 
 
841
 
 
842
Calculate useful statistics over a dimension of a piddle
 
843
 
 
844
=for usage
 
845
 
 
846
  ($mean, $rms, $median, $min, $max, $adev) = statover($piddle, $weights);
 
847
 
 
848
This utility function calculates various useful
 
849
quantities of a piddle. These are the mean:
 
850
 
 
851
  MEAN = sum (x)/ N
 
852
 
 
853
with C<N> being the number of elements in x, the root mean
 
854
square deviation from the mean, RMS, given as,
 
855
 
 
856
  RMS = sqrt(sum( (x-mean(x))^2 )/(N-1));
 
857
 
 
858
Note the use of C<N-1> which for almost all cases should be
 
859
the right normalisation factor. The routine also returns 
 
860
the median, minimum and maximum of the piddle as well as
 
861
the mean absolute deviation, defined as:
 
862
 
 
863
  ADEV = sqrt(sum( abs(x-mean(x)) )/N)
 
864
 
 
865
note here that we use the mean and not the median. This could
 
866
possibly be changed in future versions of the code. 
 
867
 
 
868
This operator is a projection operator so the calculation
 
869
will take place over the final dimension. Thus if the input 
 
870
is N-dimensional each returned value will be N-1 dimensional, 
 
871
to calculate the statistics for the entire piddle either
 
872
use C<clump(-1)> directly on the piddle or call C<stats>.
 
873
 
 
874
 
 
875
',
 
876
     BadDoc =>
 
877
'
 
878
Bad values are simply ignored in the calculation and if all data are
 
879
bad the results will also be bad.
 
880
',
 
881
);
 
882
 
 
883
pp_add_exported('','stats');
 
884
pp_addpm(<<'EOD');
 
885
 
 
886
=head2 stats
 
887
 
 
888
=for ref
 
889
 
 
890
Calculates useful statistics on a piddle
 
891
 
 
892
=for usage
 
893
 
 
894
 ($mean,$rms,$median,$min,$max) = stats($piddle,[$weights]);
 
895
 
 
896
This utility calculates all the most useful
 
897
quantities in one call.
 
898
 
 
899
B<Note:> The RMS value that this function returns in the RMS 
 
900
deviation from the mean, also known as the population standard-
 
901
deviation.
 
902
 
 
903
EOD
 
904
 
 
905
    if ( $bvalflag ) {
 
906
        pp_addpm(<<'EOD');
 
907
=for bad
 
908
 
 
909
The return values if all elements are bad is currently poorly
 
910
defined.
 
911
 
 
912
EOD
 
913
} # if: bvalflag
 
914
pp_addpm(<<'EOD');
 
915
=cut
 
916
 
 
917
*stats    = \&PDL::stats;
 
918
sub PDL::stats {
 
919
    barf('Usage: ($mean,[$rms]) = stats($data,[$weights])') if $#_>1;
 
920
    my ($data,$weights) = @_;
 
921
    my ($mean,$rms);
 
922
    if ($#_==0) {
 
923
EOD
 
924
 
 
925
    if ( $bvalflag ) {
 
926
        pp_addpm(<<'!NO!SUBS!');
 
927
        my $npts = $data->ngood;
 
928
!NO!SUBS!
 
929
} else {
 
930
        pp_addpm(<<'!NO!SUBS!');
 
931
        my $npts = $data->nelem;
 
932
!NO!SUBS!
 
933
 
 
934
} # if: $bvalflag
 
935
 
 
936
       pp_addpm(<<'EOD');
 
937
       if ( $npts == 0 ) {
 
938
           $mean = 0.0; $rms = 0.0;
 
939
       } else {
 
940
           $mean = $data->dsum / $npts;
 
941
           $rms  = sqrt( ((($data-$mean)**2 )->dsum) / $npts );
 
942
       }
 
943
    }
 
944
    else {
 
945
       my $wtsum = $weights->dsum;
 
946
       if ( $wtsum == 0.0 ) {
 
947
           $mean = 0.0; $rms = 0.0;
 
948
       } else {
 
949
           $mean = (($weights*$data)->dsum) / $wtsum;
 
950
           $rms = sqrt( ( ( $weights*(($data-$mean)**2) )->dsum )  / $wtsum );
 
951
       }
 
952
    }
 
953
    my ($median,$min,$max) = ($data->median,$data->min,$data->max);
 
954
    print "Mean = $mean, RMS = $rms, Median = $median\n".
 
955
          "Min  = $min, Max = $max\n" if $PDL::verbose;
 
956
    return $mean unless wantarray;
 
957
    return ($mean,$rms,$median,$min,$max);
 
958
}
 
959
EOD
 
960
 
 
961
for(
 
962
    {Name => 'histogram',
 
963
     WeightPar => '',
 
964
     HistType => 'int+',
 
965
     HistOp => '++',
 
966
     Doc1 => "",
 
967
     Doc2 => "",
 
968
     Doc3 => "number of\n",
 
969
     Doc4 => "\nUse L<hist|PDL::Basic/hist> instead for a high-level interface.\n",
 
970
     Doc5 => "histogram(pdl(1,1,2),1,0,3)\n [0 2 1]"
 
971
     },
 
972
    {Name => 'whistogram',
 
973
     WeightPar => 'float+ wt(n);',
 
974
     HistType => 'float+',
 
975
     HistOp => '+= $wt()',
 
976
     Doc1 => " from weighted data",
 
977
     Doc2 => "\$weights, ",
 
978
     Doc3 => "sum of the values in C<\$weights>\nthat correspond to ",
 
979
     Doc4 => "",
 
980
     Doc5 => "whistogram(pdl(1,1,2), pdl(0.1,0.1,0.5), 1, 0, 4)\n [0 0.2 0.5 0]"
 
981
     }
 
982
    )
 
983
{
 
984
pp_def($_->{Name},
 
985
       Pars => 'in(n); '.$_->{WeightPar}.$_->{HistType}.  '[o] hist(m)',
 
986
       # set outdim by Par!
 
987
       OtherPars => 'double step; double min; int msize => m',
 
988
       HandleBad => 1,
 
989
       Code => 
 
990
       'register int j;
 
991
        register int maxj = $SIZE(m)-1;
 
992
        register double min  = $COMP(min);
 
993
        register double step = $COMP(step);
 
994
        threadloop %{
 
995
           loop(m) %{ $hist() = 0; %}
 
996
        %}
 
997
        threadloop %{
 
998
           loop(n) %{
 
999
              j = (int) (($in()-min)/step);
 
1000
              if (j<0) j=0;
 
1001
              if (j > maxj) j = maxj;
 
1002
              ($hist(m => j))'.$_->{HistOp}.';
 
1003
           %}
 
1004
        %}',
 
1005
       BadCode => 
 
1006
       'register int j;
 
1007
        register int maxj = $SIZE(m)-1;
 
1008
        register double min  = $COMP(min);
 
1009
        register double step = $COMP(step);
 
1010
        threadloop %{
 
1011
           loop(m) %{ $hist() = 0; %}
 
1012
        %}
 
1013
        threadloop %{
 
1014
           loop(n) %{
 
1015
              if ( $ISGOOD(in()) ) {
 
1016
                 j = (int) (($in()-min)/step);
 
1017
                 if (j<0) j=0;
 
1018
                 if (j > maxj) j = maxj;
 
1019
                 ($hist(m => j))'.$_->{HistOp}.';
 
1020
              }
 
1021
           %}
 
1022
        %}',
 
1023
Doc=><<"EOD");
 
1024
 
 
1025
=for ref
 
1026
 
 
1027
Calculates a histogram$_->{Doc1} for given stepsize and minimum.
 
1028
 
 
1029
=for usage
 
1030
 
 
1031
 \$h = $_->{Name}(\$data, $_->{Doc2}\$step, \$min, \$numbins);
 
1032
 \$hist = zeroes \$numbins;  # Put histogram in existing piddle.
 
1033
 $_->{Name}(\$data, $_->{Doc2}\$hist, \$step, \$min, \$numbins);
 
1034
 
 
1035
The histogram will contain C<\$numbins> bins starting from C<\$min>, each
 
1036
C<\$step> wide. The value in each bin is the $_->{Doc3}values in C<\$data> that lie within the bin limits.
 
1037
 
 
1038
Data below the lower limit is put in the first bin, and data above the
 
1039
upper limit is put in the last bin.
 
1040
 
 
1041
The output is reset in a different threadloop so that you
 
1042
can take a histogram of C<\$a(10,12)> into C<\$b(15)> and get the result
 
1043
you want.
 
1044
$_->{Doc4}
 
1045
 
 
1046
=for example
 
1047
 
 
1048
 perldl> p $_->{Doc5}
 
1049
 
 
1050
EOD
 
1051
}
 
1052
 
 
1053
for(
 
1054
    {Name => 'histogram2d',
 
1055
     WeightPar => '',
 
1056
     HistType => 'int+',
 
1057
     HistOp => '++',
 
1058
     Doc1 => "",
 
1059
     Doc2 => "",
 
1060
     Doc3 => "number of\n",
 
1061
     Doc5 => "histogram2d(pdl(1,1,1,2,2),pdl(2,1,1,1,1),1,0,3,1,0,3)
 
1062
 [
 
1063
  [0 0 0]
 
1064
  [0 2 2]
 
1065
  [0 1 0]
 
1066
 ]
 
1067
 
 
1068
"},
 
1069
    {Name => 'whistogram2d',
 
1070
     WeightPar => 'float+ wt(n);',
 
1071
     HistType => 'float+',
 
1072
     HistOp => '+= $wt()',
 
1073
     Doc1 => " from weighted data",
 
1074
     Doc2 => " \$weights,",
 
1075
     Doc3 => "sum of the values in\nC<\$weights> that correspond to ",
 
1076
     Doc5 => "whistogram2d(pdl(1,1,1,2,2),pdl(2,1,1,1,1),pdl(0.1,0.2,0.3,0.4,0.5),1,0,3,1,0,3)
 
1077
 [
 
1078
  [  0   0   0]
 
1079
  [  0 0.5 0.9]
 
1080
  [  0 0.1   0]
 
1081
 ]
 
1082
 
 
1083
"}
 
1084
    )
 
1085
{
 
1086
pp_def($_->{Name},
 
1087
       Pars => 'ina(n); inb(n); '.$_->{WeightPar}.$_->{HistType}.  '[o] hist(ma,mb)',
 
1088
       # set outdim by Par!
 
1089
       OtherPars => 'double stepa; double mina; int masize => ma;
 
1090
                     double stepb; double minb; int mbsize => mb;',
 
1091
       HandleBad => 1,
 
1092
       Code => 
 
1093
       'register int ja,jb;
 
1094
        register int maxja = $SIZE(ma)-1;
 
1095
        register int maxjb = $SIZE(mb)-1;
 
1096
        register double mina = $COMP(mina);
 
1097
        register double minb = $COMP(minb);
 
1098
        register double stepa = $COMP(stepa);
 
1099
        register double stepb = $COMP(stepb);
 
1100
        threadloop %{
 
1101
           loop(ma,mb) %{ $hist() = 0; %}
 
1102
        %}
 
1103
        threadloop %{
 
1104
           loop(n) %{
 
1105
              ja = (int) (($ina()-mina)/stepa);
 
1106
              jb = (int) (($inb()-minb)/stepb);
 
1107
              if (ja<0) ja=0;
 
1108
              if (ja > maxja) ja = maxja;
 
1109
              if (jb<0) jb=0;
 
1110
              if (jb > maxjb) jb = maxjb;
 
1111
              ($hist(ma => ja,mb => jb))'.$_->{HistOp}.';
 
1112
           %}
 
1113
        %}
 
1114
        ',
 
1115
       BadCode => 
 
1116
       'register int ja,jb;
 
1117
        register int maxja = $SIZE(ma)-1;
 
1118
        register int maxjb = $SIZE(mb)-1;
 
1119
        register double mina = $COMP(mina);
 
1120
        register double minb = $COMP(minb);
 
1121
        register double stepa = $COMP(stepa);
 
1122
        register double stepb = $COMP(stepb);
 
1123
        threadloop %{
 
1124
           loop(ma,mb) %{ $hist() = 0; %}
 
1125
        %}
 
1126
        threadloop %{
 
1127
           loop(n) %{
 
1128
              if ( $ISGOOD(ina()) && $ISGOOD(inb()) ) {
 
1129
                 ja = (int) (($ina()-mina)/stepa);
 
1130
                 jb = (int) (($inb()-minb)/stepb);
 
1131
                 if (ja<0) ja=0;
 
1132
                 if (ja > maxja) ja = maxja;
 
1133
                 if (jb<0) jb=0;
 
1134
                 if (jb > maxjb) jb = maxjb;
 
1135
                 ($hist(ma => ja,mb => jb))'.$_->{HistOp}.';
 
1136
              }
 
1137
           %}
 
1138
        %}
 
1139
        ',
 
1140
Doc=><<"EOD");
 
1141
 
 
1142
=for ref
 
1143
 
 
1144
Calculates a 2d histogram$_->{Doc1}.
 
1145
 
 
1146
=for usage
 
1147
 
 
1148
 \$h = $_->{Name}(\$datax, \$datay,$_->{Doc2}
 
1149
       \$stepx, \$minx, \$nbinx, \$stepy, \$miny, \$nbiny);
 
1150
 \$hist = zeroes \$nbinx, \$nbiny;  # Put histogram in existing piddle.
 
1151
 $_->{Name}(\$datax, \$datay,$_->{Doc2} \$hist,
 
1152
       \$stepx, \$minx, \$nbinx, \$stepy, \$miny, \$nbiny);
 
1153
 
 
1154
The histogram will contain C<\$nbinx> x C<\$nbiny> bins, with the lower
 
1155
limits of the first one at C<(\$minx, \$miny)>, and with bin size
 
1156
C<(\$stepx, \$stepy)>. 
 
1157
The value in each bin is the $_->{Doc3}values in C<\$datax> and C<\$datay> that lie within the bin limits.
 
1158
 
 
1159
Data below the lower limit is put in the first bin, and data above the
 
1160
upper limit is put in the last bin.
 
1161
 
 
1162
=for example
 
1163
 
 
1164
 perldl> p $_->{Doc5}
 
1165
 
 
1166
EOD
 
1167
}
 
1168
 
 
1169
 
 
1170
###########################################################
 
1171
# a number of constructors: fibonacci, append, axisvalues &
 
1172
# random numbers
 
1173
###########################################################
 
1174
 
 
1175
pp_def('fibonacci',
 
1176
        Pars => '[o]x(n);',
 
1177
        Doc=>'Constructor - a vector with Fibonacci\'s sequence',
 
1178
        PMFunc=>'',
 
1179
        PMCode=><<'EOD',
 
1180
sub fibonacci { ref($_[0]) && ref($_[0]) ne 'PDL::Type' ? $_[0]->fibonacci : PDL->fibonacci(@_) }
 
1181
sub PDL::fibonacci{
 
1182
   my $class = shift;
 
1183
   my $x = scalar(@_)? $class->new_from_specification(@_) : $class->new_or_inplace;
 
1184
   &PDL::_fibonacci_int($x->clump(-1));
 
1185
   return $x;
 
1186
}
 
1187
EOD
 
1188
     Code => '
 
1189
        PDL_Long i=0;
 
1190
        $GENERIC() x1, x2;
 
1191
 
 
1192
        x1 = 1; x2 = 0;
 
1193
 
 
1194
        loop(n) %{
 
1195
           $x() = x1 + x2;
 
1196
           if (i++>0) {
 
1197
              x2 = x1;
 
1198
              x1 = $x();
 
1199
           }
 
1200
        %}
 
1201
');
 
1202
 
 
1203
pp_def('append',
 
1204
        Pars => 'a(n); b(m); [o] c(mn)',
 
1205
# note that ideally we want to say '$SIZE(mn) = $SIZE(m)+$SIZE(n);'
 
1206
# but that requires placing RedoDimsParsedCode *after* assignment of
 
1207
# childdims to $SIZE(XXX)!!!  XXXXXmake that workXXXXX
 
1208
        RedoDimsCode => '
 
1209
                pdl * dpdla = $PDL(a);
 
1210
                pdl * dpdlb = $PDL(b);
 
1211
                $SIZE(mn) = (dpdla->ndims > 0 ? dpdla->dims[0] : 1) + 
 
1212
                        (dpdlb->ndims > 0 ? dpdlb->dims[0] : 1);
 
1213
                ',
 
1214
        Code => 'register PDL_Long mnp;
 
1215
                 PDL_Long ns = $SIZE(n);
 
1216
                 threadloop %{
 
1217
                       loop(n) %{ $c(mn => n) = $a(); %}
 
1218
                       loop(m) %{ mnp = m+ns; $c(mn => mnp) = $b(); %}
 
1219
                 %}',
 
1220
        Doc =>
 
1221
'=for ref
 
1222
 
 
1223
append two piddles by concantening along their respective first dimensions
 
1224
 
 
1225
=for example
 
1226
 
 
1227
 $a = ones(2,4,7);
 
1228
 $b = sequence 5;
 
1229
 $c = $a->append($b);  # size of $c is now (7,4,7) (a jumbo-piddle ;)
 
1230
 
 
1231
C<append> appends two piddles along their first dims. Rest of the dimensions
 
1232
must be compatible in the threading sense. Resulting size of first dim is
 
1233
the sum of the sizes of the first dims of the two argument piddles -
 
1234
ie C<n + m>.
 
1235
 
 
1236
'
 
1237
   );
 
1238
 
 
1239
pp_def( 'axisvalues',
 
1240
        Pars => '[o,nc]a(n)',
 
1241
        Code => 'loop(n) %{ $a() = n; %}',
 
1242
        Doc => '
 
1243
=for ref
 
1244
 
 
1245
Internal routine
 
1246
 
 
1247
C<axisvalues> is the internal primitive that implements 
 
1248
L<axisvals|PDL::Basic/axisvals> 
 
1249
and alters its argument.
 
1250
 
 
1251
'
 
1252
       ); # pp_def: axisvalues
 
1253
 
 
1254
 
 
1255
pp_addpm(<<'EOD');
 
1256
 
 
1257
=head2 random
 
1258
 
 
1259
=for ref
 
1260
 
 
1261
Constructor which returns piddle of random numbers
 
1262
 
 
1263
=for usage
 
1264
 
 
1265
 $a = random([type], $nx, $ny, $nz,...);
 
1266
 $a = random $b;
 
1267
 
 
1268
etc (see L<zeroes|PDL::Core/zeroes>).
 
1269
 
 
1270
This is the uniform distribution between 0 and 1 (assumedly
 
1271
excluding 1 itself). The arguments are the same as C<zeroes>
 
1272
(q.v.) - i.e. one can specify dimensions, types or give
 
1273
a template.
 
1274
 
 
1275
You can use the perl function L<srand|perlfunc/srand> to seed the random
 
1276
generator. For further details consult Perl's  L<srand|perlfunc/srand>
 
1277
documentation.
 
1278
 
 
1279
=head2 randsym
 
1280
 
 
1281
=for ref
 
1282
 
 
1283
Constructor which returns piddle of random numbers
 
1284
 
 
1285
=for usage
 
1286
 
 
1287
 $a = randsym([type], $nx, $ny, $nz,...);
 
1288
 $a = randsym $b;
 
1289
 
 
1290
etc (see L<zeroes|PDL::Core/zeroes>).
 
1291
 
 
1292
This is the uniform distribution between 0 and 1 (excluding both 0 and
 
1293
1, cf L<random|/random>). The arguments are the same as C<zeroes> (q.v.) -
 
1294
i.e. one can specify dimensions, types or give a template.
 
1295
 
 
1296
You can use the perl function L<srand|perlfunc/srand> to seed the random
 
1297
generator. For further details consult Perl's  L<srand|perlfunc/srand>
 
1298
documentation.
 
1299
 
 
1300
=cut
 
1301
EOD
 
1302
 
 
1303
pp_addhdr(<<'EOH');
 
1304
 
 
1305
#ifndef Drand01
 
1306
#define Drand01() (((double)rand()) / (RAND_MAX+1.0))
 
1307
#endif
 
1308
 
 
1309
EOH
 
1310
 
 
1311
pp_def(
 
1312
        'random',
 
1313
        Pars=>'a();',
 
1314
        PMFunc => '',
 
1315
        Code =>
 
1316
        '$a() = Drand01();',
 
1317
        Doc=>undef,
 
1318
        PMCode=><<'EOD',
 
1319
sub random { ref($_[0]) && ref($_[0]) ne 'PDL::Type' ? $_[0]->random : PDL->random(@_) }
 
1320
sub PDL::random {
 
1321
   my $class = shift;
 
1322
   my $x = scalar(@_)? $class->new_from_specification(@_) : $class->new_or_inplace;
 
1323
   &PDL::_random_int($x);
 
1324
   return $x;
 
1325
}
 
1326
EOD
 
1327
);
 
1328
 
 
1329
pp_def(
 
1330
        'randsym',
 
1331
        Pars=>'a();',
 
1332
        PMFunc => '',
 
1333
        Code =>
 
1334
        'double tmp;
 
1335
         do tmp = Drand01(); while (tmp == 0.0); /* 0 < tmp < 1 */
 
1336
         $a() = tmp;',
 
1337
        Doc=>undef,
 
1338
        PMCode=><<'EOD',
 
1339
sub randsym { ref($_[0]) && ref($_[0]) ne 'PDL::Type' ? $_[0]->randsym : PDL->randsym(@_) }
 
1340
sub PDL::randsym {
 
1341
   my $class = shift;
 
1342
   my $x = scalar(@_)? $class->new_from_specification(@_) : $class->new_or_inplace;
 
1343
   &PDL::_randsym_int($x);
 
1344
   return $x;
 
1345
}
 
1346
EOD
 
1347
);
 
1348
 
 
1349
pp_addpm(<<'EOD');
 
1350
 
 
1351
=head2 grandom
 
1352
 
 
1353
=for ref
 
1354
 
 
1355
Constructor which returns piddle of Gaussian random numbers
 
1356
 
 
1357
=for usage
 
1358
 
 
1359
 $a = grandom([type], $nx, $ny, $nz,...);
 
1360
 $a = grandom $b;
 
1361
 
 
1362
etc (see L<zeroes|PDL::Core/zeroes>).
 
1363
 
 
1364
This is generated using the math library routine C<ndtri>.
 
1365
 
 
1366
Mean = 0, Stddev = 1
 
1367
 
 
1368
 
 
1369
You can use the perl function L<srand|perlfunc/srand> to seed the random
 
1370
generator. For further details consult Perl's  L<srand|perlfunc/srand>
 
1371
documentation.
 
1372
 
 
1373
=cut
 
1374
 
 
1375
sub grandom { ref($_[0]) && ref($_[0]) ne 'PDL::Type' ? $_[0]->grandom : PDL->grandom(@_) }
 
1376
sub PDL::grandom {
 
1377
   my $class = shift;
 
1378
   my $x = scalar(@_)? $class->new_from_specification(@_) : $class->new_or_inplace;
 
1379
   use PDL::Math 'ndtri';
 
1380
   $x .= ndtri(randsym($x));
 
1381
   return $x;
 
1382
}
 
1383
 
 
1384
EOD
 
1385
 
 
1386
pp_add_exported('','grandom');
 
1387
 
 
1388
###############################################################
 
1389
# routines somehow related to interpolation
 
1390
###############################################################
 
1391
 
 
1392
# The last x is ignored...
 
1393
pp_def('vsearch',
 
1394
       HandleBad => 0,
 
1395
       BadDoc => 'needs major (?) work to handles bad values',
 
1396
        Pars => 'i(); x(n); int [o]ip()',
 
1397
        GenericTypes => ['F','D'], # too restrictive ?
 
1398
        Code => 'int carp=0;
 
1399
                 threadloop %{
 
1400
                   long n1 = $SIZE(n)-1;
 
1401
                   long jl=-1, jh=n1, m;
 
1402
                   int up = ($x(n => n1) > $x(n => 0));
 
1403
                   $GENERIC() d;
 
1404
                   while (jh-jl > 1)  /* binary search */
 
1405
                        {
 
1406
                                m = (jh+jl) >> 1;
 
1407
                                if ($i() > $x(n => m) == up)
 
1408
                                        jl = m;
 
1409
                                else
 
1410
                                        jh = m;
 
1411
                        }
 
1412
                   if (jl == -1) {
 
1413
                        jh = 0;
 
1414
                   } else if (jl == n1) {
 
1415
                        if ($i() != $x(n => n1)) carp = 1;
 
1416
                        jh = n1;
 
1417
                   } else {
 
1418
                        jh = jl+1;
 
1419
                   }
 
1420
                   $ip() = jh;
 
1421
                %}
 
1422
                if (carp) warn("some values had to be extrapolated");
 
1423
', Doc=><<'EOD');
 
1424
 
 
1425
=for ref
 
1426
 
 
1427
routine for searching 1D values i.e. step-function interpolation.
 
1428
 
 
1429
=for usage
 
1430
 
 
1431
 $inds = vsearch($vals, $xs);
 
1432
 
 
1433
Returns for each value of C<$vals> the index of the least larger member
 
1434
of C<$xs> (which need to be in increasing order). If the value is larger
 
1435
than any member of C<$xs>, the index to the last element of C<$xs> is 
 
1436
returned.
 
1437
 
 
1438
=for example
 
1439
 
 
1440
This function is useful e.g. when you have a list of probabilities
 
1441
for events and want to generate indices to events:
 
1442
 
 
1443
 $a = pdl(.01,.86,.93,1); # Barnsley IFS probabilities cumulatively
 
1444
 $b = random 20;
 
1445
 $c = vsearch($b, $a); # Now, $c will have the appropriate distr.
 
1446
 
 
1447
It is possible to use the L<cumusumover|/cumusumover> function to obtain
 
1448
cumulative probabilities from absolute probabilities.
 
1449
 
 
1450
EOD
 
1451
 
 
1452
pp_def('interpolate',
 
1453
       HandleBad => 0,
 
1454
       BadDoc => 'needs major (?) work to handles bad values',
 
1455
        Pars => 'xi(); x(n); y(n); [o] yi(); int [o] err()',
 
1456
        GenericTypes => ['F','D'], # too restrictive ?
 
1457
        Code => '
 
1458
                 $GENERIC() d;
 
1459
                 long n  = $SIZE(n);
 
1460
                 long n1 = n-1;
 
1461
                 int up = ($x(n => n1) > $x(n => 0));
 
1462
                 long jl, jh, m;
 
1463
                 int carp;
 
1464
 
 
1465
                 threadloop %{
 
1466
                   jl = -1;
 
1467
                   jh = n;
 
1468
                   carp = 0;
 
1469
                   while (jh-jl > 1)  /* binary search */
 
1470
                        {
 
1471
                                m = (jh+jl) >> 1;
 
1472
                                if ($xi() > $x(n => m) == up)
 
1473
                                        jl = m;
 
1474
                                else
 
1475
                                        jh = m;
 
1476
                        }
 
1477
                   if (jl == -1) {
 
1478
                        if ($xi() != $x(n => 0)) carp = 1;
 
1479
                        jl = 0;
 
1480
                   } else if (jh == n) {
 
1481
                        if ($xi() != $x(n => n1)) carp = 1;
 
1482
                        jl = n1-1;
 
1483
                   }
 
1484
                   jh = jl+1;
 
1485
                   if ((d = $x(n => jh)-$x(n => jl)) == 0)
 
1486
                        barf("identical abscissas");
 
1487
                   d = ($x(n => jh)-$xi())/d;
 
1488
                   $yi() = d*$y(n => jl) + (1-d)*$y(n => jh);
 
1489
                   $err() = carp;
 
1490
                %}
 
1491
', Doc=><<'EOD');
 
1492
 
 
1493
=for ref
 
1494
 
 
1495
routine for 1D linear interpolation
 
1496
 
 
1497
=for usage
 
1498
 
 
1499
 ( $yi, $err ) = interpolate($xi, $x, $y)
 
1500
 
 
1501
Given a set of points C<($x,$y)>, use linear interpolation
 
1502
to find the values C<$yi> at a set of points C<$xi>.
 
1503
 
 
1504
C<interpolate> uses a binary search to find the suspects, er...,
 
1505
interpolation indices and therefore abscissas (ie C<$x>)
 
1506
have to be I<strictly> ordered (increasing or decreasing). 
 
1507
For interpolation at lots of
 
1508
closely spaced abscissas an approach that uses the last index found as
 
1509
a start for the next search can be faster (compare Numerical Recipes
 
1510
C<hunt> routine). Feel free to implement that on top of the binary
 
1511
search if you like. For out of bounds values it just does a linear
 
1512
extrapolation and sets the corresponding element of C<$err> to 1,
 
1513
which is otherwise 0.
 
1514
 
 
1515
See also L<interpol|/interpol>, which uses the same routine,
 
1516
differing only in the handling of extrapolation - an error message
 
1517
is printed rather than returning an error piddle.
 
1518
 
 
1519
=cut
 
1520
EOD
 
1521
 
 
1522
pp_add_exported('', 'interpol');
 
1523
pp_addpm(<<'EOD');
 
1524
 
 
1525
=head2 interpol
 
1526
 
 
1527
=for sig
 
1528
 
 
1529
 Signature: (xi(); x(n); y(n); [o] yi())
 
1530
 
 
1531
=for ref
 
1532
 
 
1533
routine for 1D linear interpolation
 
1534
 
 
1535
=for usage
 
1536
 
 
1537
 $yi = interpol($xi, $x, $y)
 
1538
 
 
1539
C<interpol> uses the same search method as L<interpolate|/interpolate>,
 
1540
hence C<$x> must be I<strictly> ordered (either increasing or decreasing).
 
1541
The difference occurs in the handling of out-of-bounds values; here
 
1542
an error message is printed.
 
1543
 
 
1544
=cut
 
1545
 
 
1546
# kept in for backwards compatability
 
1547
sub interpol ($$$;$) {
 
1548
    my $xi = shift;
 
1549
    my $x  = shift;
 
1550
    my $y  = shift;
 
1551
 
 
1552
    my $yi;
 
1553
    if ( $#_ == 0 ) { $yi = $_[0]; }
 
1554
    else            { $yi = PDL->null; }
 
1555
 
 
1556
    interpolate( $xi, $x, $y, $yi, my $err = PDL->null );
 
1557
    print "some values had to be extrapolated\n"
 
1558
        if any $err;
 
1559
 
 
1560
    return $yi if $#_ == -1;
 
1561
 
 
1562
} # sub: interpol()
 
1563
*PDL::interpol = \&interpol;
 
1564
 
 
1565
EOD
 
1566
 
 
1567
 
 
1568
##############################################################
 
1569
# things related to indexing: one2nd, which, where
 
1570
##############################################################
 
1571
 
 
1572
pp_add_exported("", 'one2nd');
 
1573
pp_addpm(<<'EOD');
 
1574
 
 
1575
=head2 one2nd
 
1576
 
 
1577
=for ref
 
1578
 
 
1579
Converts a one dimensional index piddle to a set of ND coordinates
 
1580
 
 
1581
=for usage
 
1582
 
 
1583
 @coords=one2nd($a, $indices)
 
1584
 
 
1585
returns an array of piddles containing the ND indexes corresponding to
 
1586
the one dimensional list indices. The indices are assumed to correspond
 
1587
to array C<$a> clumped using C<clump(-1)>. This routine is used in 
 
1588
L<whichND|/whichND>,
 
1589
but is useful on its own occasionally.
 
1590
 
 
1591
=for example
 
1592
 
 
1593
 perldl> $a=pdl [[[1,2],[-1,1]], [[0,-3],[3,2]]]; $c=$a->clump(-1)
 
1594
 perldl> $maxind=maximum_ind($c); p $maxind;
 
1595
 6
 
1596
 perldl> print one2nd($a, maximum_ind($c))
 
1597
 0 1 1
 
1598
 perldl> p $a->at(0,1,1)
 
1599
 3
 
1600
 
 
1601
=cut
 
1602
 
 
1603
*one2nd = \&PDL::one2nd;
 
1604
sub PDL::one2nd {
 
1605
  barf "Usage: one2nd $array $indices\n" if $#_ != 1;
 
1606
  my ($a, $ind)=@_;
 
1607
  my @dimension=$a->dims;
 
1608
  my(@index);
 
1609
  my $count=0;
 
1610
  foreach (@dimension) {
 
1611
    $index[$count++]=$ind % $_;
 
1612
    $ind=long($ind/$_);
 
1613
  }
 
1614
  return @index;
 
1615
}
 
1616
 
 
1617
EOD
 
1618
 
 
1619
my $doc_which = <<'EOD';
 
1620
 
 
1621
=for ref
 
1622
 
 
1623
Returns piddle of indices of non-zero values.
 
1624
 
 
1625
=for usage
 
1626
 
 
1627
 $i = which($mask);
 
1628
 
 
1629
returns a pdl with indices for all those elements that are
 
1630
nonzero in the mask. Note that the returned indices will be 1D. If you want
 
1631
to index into the original mask or a similar piddle remember to flatten
 
1632
it before calling index:
 
1633
 
 
1634
  $data = random 5, 5;
 
1635
  $idx = which $data > 0.5; # $idx is now 1D
 
1636
  $bigsum = $data->flat->index($idx)->sum;  # flatten before indexing
 
1637
 
 
1638
Compare also L<where|/where> for similar functionality.
 
1639
 
 
1640
If you want to return both the indices of non-zero values and the
 
1641
complement, use the function L<which_both|/which_both>.
 
1642
 
 
1643
=for example
 
1644
 
 
1645
 perldl> $x = sequence(10); p $x
 
1646
 [0 1 2 3 4 5 6 7 8 9]
 
1647
 perldl> $indx = which($x>6); p $indx
 
1648
 [7 8 9]
 
1649
 
 
1650
EOD
 
1651
 
 
1652
my $doc_which_both = <<'EOD';
 
1653
 
 
1654
=for ref
 
1655
 
 
1656
Returns piddle of indices of non-zero values and their complement
 
1657
 
 
1658
=for usage
 
1659
 
 
1660
 ($i, $c_i) = which_both($mask);
 
1661
 
 
1662
This works just as L<which|/which>, but the complement of C<$i> will be in
 
1663
C<$c_i>.
 
1664
 
 
1665
=for example
 
1666
 
 
1667
 perldl> $x = sequence(10); p $x
 
1668
 [0 1 2 3 4 5 6 7 8 9]
 
1669
 perldl> ($small, $big) = which_both ($x >= 5); p "$small\n $big"
 
1670
 [5 6 7 8 9]
 
1671
 [0 1 2 3 4]
 
1672
 
 
1673
EOD
 
1674
 
 
1675
    for (
 
1676
         {Name=>'which',
 
1677
          Pars => 'mask(n); int [o] inds(m);',
 
1678
          Variables => 'int dm=0;',
 
1679
          Elseclause => "",
 
1680
          Autosize => '$SIZE(m) = sum;',
 
1681
          Doc => $doc_which,
 
1682
          PMCode=><<'EOD',
 
1683
   sub which { my ($this,$out) = @_;
 
1684
                $this = $this->flat;
 
1685
                $out = $this->nullcreate unless defined $out;
 
1686
                PDL::_which_int($this,$out);
 
1687
                return $out;
 
1688
   }
 
1689
   *PDL::which = \&which;
 
1690
EOD
 
1691
          },
 
1692
         {Name => 'which_both',
 
1693
          Pars => 'mask(n); int [o] inds(m); int [o]notinds(q)',
 
1694
          Variables => 'int dm=0; int dm2=0;',
 
1695
          Elseclause => "else { \n          \$notinds(q => dm2)=n; \n           dm2++;\n     }",
 
1696
          Autosize => '$SIZE(m) = sum;'."\n".'            $SIZE(q) = dpdl->dims[0]-sum;',
 
1697
          Doc => $doc_which_both,
 
1698
          PMCode=><<'EOD',
 
1699
   sub which_both { my ($this,$outi,$outni) = @_;
 
1700
                $this = $this->flat;
 
1701
                $outi = $this->nullcreate unless defined $outi; 
 
1702
                $outni = $this->nullcreate unless defined $outni;
 
1703
                PDL::_which_both_int($this,$outi,$outni);
 
1704
                return wantarray ? ($outi,$outni) : $outi;
 
1705
   }
 
1706
   *PDL::which_both = \&which_both;
 
1707
EOD
 
1708
          }
 
1709
         )
 
1710
{
 
1711
    pp_def($_->{Name},
 
1712
           Doc => $_->{Doc},
 
1713
           Pars => $_->{Pars},
 
1714
           PMCode => $_->{PMCode},
 
1715
           Code => $_->{Variables} .
 
1716
                 'loop(n) %{
 
1717
                        if ($mask()) {
 
1718
                                $inds(m => dm) = n;
 
1719
                                dm++;
 
1720
                        }'.$_->{Elseclause} . "\n".
 
1721
                ' %}',
 
1722
#               the next one is currently a dirty hack
 
1723
#               this will probably break once dataflow is enabled again
 
1724
#               *unless* we have made sure that mask is physical by now!!!
 
1725
           RedoDimsCode => '
 
1726
                PDL_Long sum = 0;
 
1727
                /* not sure if this is necessary */
 
1728
                pdl * dpdl = $PDL(mask);
 
1729
                $GENERIC() *m_datap = (($GENERIC() *)(PDL_REPRP(dpdl)));
 
1730
                PDL_Long inc = PDL_REPRINC(dpdl,0);
 
1731
                PDL_Long offs = PDL_REPROFFS(dpdl);
 
1732
                int i;
 
1733
 
 
1734
                if (dpdl->ndims != 1)
 
1735
                  barf("dimflag currently works only with 1D pdls");
 
1736
 
 
1737
                for (i=0; i<dpdl->dims[0]; i++) {
 
1738
                       if ( *(m_datap+inc*i+offs)) sum++;
 
1739
                }
 
1740
 
 
1741
                '.$_->{Autosize} . '
 
1742
                /* printf("RedoDimsCode: setting dim m to %ld\n",sum); */'
 
1743
           );
 
1744
}
 
1745
 
 
1746
pp_addpm(<<'EOD'
 
1747
=head2 where
 
1748
 
 
1749
=for ref
 
1750
 
 
1751
Returns indices to non-zero values or those values from another piddle.
 
1752
 
 
1753
=for usage
 
1754
 
 
1755
 $i = $x->where($x+5 > 0); # $i contains elements of $x
 
1756
                           # where mask ($x+5 > 0) is 1
 
1757
 
 
1758
Note: C<$i> is always 1-D, even if C<$x> is >1-D. The first argument
 
1759
(the values) and the second argument (the mask) currently have to have
 
1760
the same initial dimensions (or horrible things happen).
 
1761
 
 
1762
It is also possible to use the same mask for several piddles with
 
1763
the same call:
 
1764
 
 
1765
 ($i,$j,$k) = where($x,$y,$z, $x+5>0);
 
1766
 
 
1767
=cut
 
1768
 
 
1769
sub PDL::where {
 
1770
    barf "Usage: where( $pdl1, ..., $pdlN, $mask )\n" if $#_ == 0;
 
1771
 
 
1772
    if($#_ == 1) {
 
1773
        my($data,$mask) = @_;
 
1774
        $data = $_[0]->clump(-1) if $_[0]->getndims>1;
 
1775
        $mask = $_[1]->clump(-1) if $_[0]->getndims>1;
 
1776
        return $data->index($mask->which());
 
1777
    } else {
 
1778
        if($_[-1]->getndims > 1) {
 
1779
            my $mask = $_[-1]->clump(-1)->which;
 
1780
            return map {$_->clump(-1)->index($mask)} @_[0..$#_-1];
 
1781
        } else {
 
1782
            my $mask = $_[-1]->which;
 
1783
            return map {$_->index($mask)} @_[0..$#_-1];
 
1784
        }
 
1785
    }
 
1786
}
 
1787
*where = \&PDL::where;
 
1788
 
 
1789
EOD
 
1790
);
 
1791
 
 
1792
pp_add_exported("", 'where');
 
1793
 
 
1794
pp_addpm(<<'EOD'
 
1795
=head2 whichND
 
1796
 
 
1797
=for ref
 
1798
 
 
1799
Returns the coordinates for non-zero values
 
1800
 
 
1801
=for usage
 
1802
 
 
1803
 @coords=whichND($mask);
 
1804
 
 
1805
returns an array of piddles containing the coordinates of the elements
 
1806
that are non-zero in C<$mask>.
 
1807
 
 
1808
=for example
 
1809
 
 
1810
 perldl> $a=sequence(10,10,3,4)
 
1811
 perldl> ($x, $y, $z, $w)=whichND($a == 203); p $x, $y, $z, $w
 
1812
 [3] [0] [2] [0]
 
1813
 perldl> print $a->at(list(cat($x,$y,$z,$w)))
 
1814
 203
 
1815
 
 
1816
=cut
 
1817
 
 
1818
*whichND = \&PDL::whichND;
 
1819
sub PDL::whichND {
 
1820
  my $mask = shift;
 
1821
  my $ind=($mask->clump(-1))->which;
 
1822
 
 
1823
  return $mask->one2nd($ind);
 
1824
}
 
1825
 
 
1826
EOD
 
1827
);
 
1828
 
 
1829
pp_add_exported("", 'whichND');
 
1830
 
 
1831
pp_done();