File Coverage

lib/PDL/Math.pd
Criterion Covered Total %
statement 5 5 100.0
branch 10 12 83.3
condition n/a
subroutine n/a
pod n/a
total 15 17 88.2


line stmt bran cond sub pod time code
1             use strict;
2             use warnings;
3             use Config;
4             use PDL::Types qw(ppdefs ppdefs_complex types);
5             require PDL::Core::Dev;
6              
7             { # pass info back to Makefile.PL
8             # Files for each routine (.c assumed)
9             my %source = qw(
10             j0 j0
11             j1 j1
12             jn jn
13             y0 j0
14             y1 j1
15             yn yn
16             );
17             my @keys = sort keys %source;
18             my $libs = PDL::Core::Dev::get_maths_libs();
19             # Test for presence of besfuncs
20             require File::Spec::Functions;
21             my $include = qq{#include "}.File::Spec::Functions::rel2abs("$::PDLBASE/mconf.h").qq{"};
22             $source{$_} = 'system' for grep PDL::Core::Dev::trylink('', $include, "$_(1.);", $libs), qw(j0 j1 y0 y1);
23             $source{$_} = 'system' for grep PDL::Core::Dev::trylink('', $include, "$_(1,1.);", $libs), qw(jn yn);
24             my %seen; # Build object file list
25             foreach my $func (@keys) {
26             my $file = $source{$func};
27             next if $file eq 'system';
28             die "File for function $func not found\n" if $file eq '';
29             $PDL::Core::Dev::EXTRAS{$::PDLMOD}{OBJECT} .= " $::PDLBASE/$file\$(OBJ_EXT)" unless $seen{$file}++;
30             }
31             # Add support routines
32             $PDL::Core::Dev::EXTRAS{$::PDLMOD}{OBJECT} .= join '', map " $::PDLBASE/$_\$(OBJ_EXT)", qw(const mtherr polevl cpoly ndtri);
33             $PDL::Core::Dev::EXTRAS{$::PDLMOD}{INC} .= qq{ "-I$::PDLBASE"};
34             }
35              
36             my $R = [ppdefs()];
37             my $F = [map $_->ppsym, grep $_->real && !$_->integer, types()];
38             my $C = [ppdefs_complex()];
39             my @Rtypes = grep $_->real, types();
40             my @Ctypes = grep !$_->real, types();
41             my $AF = [map $_->ppsym, grep !$_->integer, types];
42             $AF = [(grep $_ ne 'D', @$AF), 'D']; # so defaults to D if non-float given
43              
44             pp_addpm({At=>'Top'},<<'EOD');
45             use strict;
46             use warnings;
47              
48             =head1 NAME
49              
50             PDL::Math - extended mathematical operations and special functions
51              
52             =head1 SYNOPSIS
53              
54             use PDL::Math;
55              
56             use PDL::Graphics::TriD;
57             imag3d [SURF2D,bessj0(rvals(zeroes(50,50))/2)];
58              
59             =head1 DESCRIPTION
60              
61             This module extends PDL with more advanced mathematical functions than
62             provided by standard Perl.
63              
64             All the functions have one input pdl, and one output, unless otherwise
65             stated.
66              
67             Many of the functions are linked from the system maths library or the
68             Cephes maths library (determined when PDL is compiled); a few are implemented
69             entirely in PDL.
70              
71             =cut
72              
73             ### Kludge for backwards compatibility with older scripts
74             ### This should be deleted at some point later than 21-Nov-2003.
75             BEGIN {use PDL::MatrixOps;}
76              
77             EOD
78              
79             # Internal doc util
80              
81             my %doco;
82             sub doco {
83             my @funcs = @_;
84             my $doc = pop @funcs;
85             for (@funcs) { $doco{$_} = $doc }
86             }
87              
88             doco (qw/acos asin atan tan/, <<'EOF');
89             The usual trigonometric function.
90             EOF
91              
92             doco (qw/cosh sinh tanh acosh asinh atanh/, <<'EOF');
93             The standard hyperbolic function.
94             EOF
95              
96             doco (qw/ceil floor/,
97             'Round to integer values in floating-point format.');
98              
99             doco ('rint',
100             q/=for ref
101              
102             Round to integer values in floating-point format.
103              
104             This is the C99 function; previous to 2.096, the doc referred to a
105             bespoke function that did banker's rounding, but this was not used
106             as a system version will have been detected and used.
107              
108             If you are looking to round half-integers up (regardless of sign), try
109             C. If you want to round half-integers away from zero,
110             try C<< ceil(abs($x)+0.5)*($x<=>0) >>./);
111              
112             doco( 'pow',"Synonym for `**'.");
113              
114             doco ('erf',"The error function.");
115             doco ('erfc',"The complement of the error function.");
116             doco ('erfi',"The inverse of the error function.");
117             doco ('ndtri',
118             "=for ref
119              
120             The value for which the area under the
121             Gaussian probability density function (integrated from
122             minus infinity) is equal to the argument (cf L).");
123              
124             doco(qw/bessj0 bessj1/,
125             "The regular Bessel function of the first kind, J_n" );
126              
127             doco(qw/bessy0 bessy1/,
128             "The regular Bessel function of the second kind, Y_n." );
129              
130             doco( qw/bessjn/,
131             '=for ref
132              
133             The regular Bessel function of the first kind, J_n
134             .
135             This takes a second int argument which gives the order
136             of the function required.
137             ');
138              
139             doco( qw/bessyn/,
140             '=for ref
141              
142             The regular Bessel function of the first kind, Y_n
143             .
144             This takes a second int argument which gives the order
145             of the function required.
146             ');
147              
148             if ($^O !~ /win32/i || $Config{cc} =~ /\bgcc/i) { # doesn't seem to be in the MS VC lib
149             doco( 'lgamma' ,<<'EOD');
150             =for ref
151              
152             log gamma function
153              
154             This returns 2 ndarrays -- the first set gives the log(gamma) values,
155             while the second set, of integer values, gives the sign of the gamma
156             function. This is useful for determining factorials, amongst other
157             things.
158              
159             EOD
160              
161             } # if: $^O !~ win32
162              
163             pp_addhdr('
164             #include
165             #include "protos.h"
166             #include "cpoly.h"
167             ');
168              
169             # Standard `-lm'
170             my (@ufuncs1) = qw(acos asin atan cosh sinh tan tanh); # F,D only
171             my (@ufuncs1g) = qw(ceil floor rint); # Any real type
172              
173             # Note:
174             # ops.pd has a power() function that does the same thing
175             # (although it has OtherPars => 'int swap;' as well)
176             # - left this in for now.
177             #
178             my (@bifuncs1) = qw(pow); # Any type
179              
180             # Extended `-lm'
181             my (@ufuncs2) = qw(acosh asinh atanh erf erfc); # F,D only
182             my (@besufuncs) = qw(j0 j1 y0 y1); # "
183             my (@besbifuncs) = qw(jn yn); # "
184             # Need igamma, ibeta, and a fall-back implementation of the above
185              
186             sub code_ufunc {
187             <
188             PDL_IF_BAD(if ( \$ISBAD(a()) ) { \$SETBAD(b()); } else,)
189             \$b() = $_[0](\$a());
190             EOF
191             }
192              
193             sub code_bifunc {
194             my $name = $_[0];
195             my $x = $_[1] || 'a'; my $y = $_[2] || 'b'; my $c = $_[3] || 'c';
196             <
197             PDL_IF_BAD(if ( \$ISBAD($x()) || \$ISBAD($y()) ) { \$SETBAD($c()); } else,)
198             \$$c() = $name(\$$x(),\$$y());
199             EOF
200             }
201              
202             foreach my $func (@ufuncs1) {
203             my $got_complex = PDL::Core::Dev::got_complex_version($func, 1);
204             pp_def($func,
205             HandleBad => 1,
206             NoBadifNaN => 1,
207             GenericTypes => [($got_complex ? @$C : ()), @$F],
208             Pars => 'a(); [o]b();',
209             Inplace => 1,
210             Doc => $doco{$func},
211             Code => code_ufunc($func),
212             );
213             }
214             # real types
215             foreach my $func (@ufuncs1g) {
216             pp_def($func,
217             HandleBad => 1,
218             NoBadifNaN => 1,
219             Pars => 'a(); [o]b();',
220             Inplace => 1,
221             Doc => $doco{$func},
222             Code => code_ufunc($func),
223             );
224             }
225              
226             foreach my $func (@bifuncs1) {
227             my $got_complex = PDL::Core::Dev::got_complex_version($func, 2);
228             pp_def($func,
229             HandleBad => 1,
230             NoBadifNaN => 1,
231             Pars => 'a(); b(); [o]c();',
232             Inplace => [ 'a' ],
233             GenericTypes => [($got_complex ? @$C : ()), @$R],
234             Doc => $doco{$func},
235             Code => code_bifunc($func),
236             );
237             }
238              
239             # Functions provided by extended -lm
240             foreach my $func (@ufuncs2) {
241             pp_def($func,
242             HandleBad => 1,
243             NoBadifNaN => 1,
244             GenericTypes => $F,
245             Pars => 'a(); [o]b();',
246             Inplace => 1,
247             Doc => $doco{$func},
248             Code => code_ufunc($func),
249             );
250             }
251              
252             foreach my $func (@besufuncs) {
253             my $fname = "bess$func";
254             pp_def($fname,
255             HandleBad => 1,
256             NoBadifNaN => 1,
257             GenericTypes => $F,
258             Pars => 'a(); [o]b();',
259             Inplace => 1,
260             Doc => $doco{$fname},
261             Code => code_ufunc($func),
262             );
263             }
264              
265             foreach my $func (@besbifuncs) {
266             my $fname = "bess$func";
267             pp_def($fname,
268             HandleBad => 1,
269             NoBadifNaN => 1,
270             GenericTypes => $F,
271             Pars => 'a(); int n(); [o]b();',
272             Inplace => [ 'a' ],
273             Doc => $doco{$fname},
274             Code => code_bifunc($func,'n','a','b'),
275             );
276             }
277              
278             if ($^O !~ /win32/i) {
279             pp_def("lgamma",
280             HandleBad => 1,
281             Pars => 'a(); [o]b(); int[o]s()',
282             Doc => $doco{"lgamma"},
283             Code => '
284             extern int signgam;
285             PDL_IF_BAD(if ( $ISBAD(a()) ) { $SETBAD(b()); $SETBAD(s()); } else,) {
286             $b() = lgamma($a());
287             $s() = signgam;
288             }
289             ', # what happens to signgam if $a() is bad?
290             );
291             } # if: os !~ win32
292              
293             elsif ($Config{cc} =~ /\bgcc/i) {
294             pp_def("lgamma",
295             HandleBad => 1,
296             Pars => 'a(); [o]b(); int[o]s()',
297             Doc => $doco{"lgamma"},
298             Code => '
299             PDL_IF_BAD(if ( $ISBAD(a()) ) { $SETBAD(b()); $SETBAD(s()); } else,) {
300             $b() = lgamma($a());
301             $s() = tgamma($a()) < 0 ? -1 : 1;
302             }
303             ', # what happens to signgam if $a() is bad?
304             );
305             } # elsif: cc =~ /\bgcc/i
306              
307             pp_def('isfinite',
308             Pars => 'a(); int [o]mask();',
309             HandleBad => 1,
310             Code => <<'EOF',
311             broadcastloop %{
312             $mask() = isfinite((double) $a()) != 0 PDL_IF_BAD(&& $ISGOOD($a()),);
313             %}
314             $PDLSTATESETGOOD(mask);
315             EOF
316             Doc =>
317             'Sets C<$mask> true if C<$a> is not a C or C (either positive or negative).',
318             BadDoc =>
319             'Bad values are treated as C or C.',
320             );
321              
322             # Extra functions from cephes
323             pp_def("erfi",
324             HandleBad => 1,
325             NoBadifNaN => 1,
326             GenericTypes => $F,
327             Pars => 'a(); [o]b()',
328             Inplace => 1,
329             Doc => "erfi",
330             Code =>
331             'extern double SQRTH;
332             PDL_IF_BAD(if ( $ISBAD(a()) ) { $SETBAD(b()); }
333             else,) { $b() = SQRTH*ndtri((1+(double)$a())/2); }',
334             );
335              
336             pp_def("ndtri",
337             HandleBad => 1,
338             NoBadifNaN => 1,
339             GenericTypes => $F,
340             Pars => 'a(); [o]b()',
341             Inplace => 1,
342             Doc => "ndtri",
343             Code =>
344             'PDL_IF_BAD(if ( $ISBAD(a()) ) { $SETBAD(b()); }
345             else,) { $b() = ndtri((double)$a()); }',
346             );
347              
348             pp_def("polyroots",
349             Pars => 'cr(n); ci(n); [o]rr(m=CALC($SIZE(n)-1)); [o]ri(m);',
350             GenericTypes => ['D'],
351             Code => <<'EOF',
352             char *fail = cpoly($P(cr), $P(ci), $SIZE(m), $P(rr), $P(ri));
353             if (fail)
354             $CROAK("cpoly: %s", fail);
355             EOF
356             PMCode => pp_line_numbers(__LINE__, <<'EOF'),
357             sub PDL::polyroots {
358             my @args = map PDL->topdl($_), @_;
359             my $natcplx = !$args[0]->type->real;
360             barf "need array context if give real data and no outputs"
361             if !$natcplx and @_ < 3 and !(wantarray//1);
362             splice @args, 0, 1, map $args[0]->$_, qw(re im) if $natcplx;
363             my @ins = splice @args, 0, 2;
364             my $explicit_out = my @outs = @args;
365             if ($natcplx) {
366             $_ //= PDL->null for $outs[0];
367             } else {
368             $_ //= PDL->null for @outs[0,1];
369             }
370             my @args_out = $natcplx ? (map PDL->null, 1..2) : @outs; # opposite from polyfromroots
371             PDL::_polyroots_int(@ins, @args_out);
372             return @args_out if !$natcplx;
373             $outs[0] .= PDL::czip(@args_out[0,1]);
374             }
375             EOF
376             Doc => '
377             =for ref
378              
379             Complex roots of a complex polynomial, given coefficients in order
380             of decreasing powers. Only works for degree >= 1.
381             Uses the Jenkins-Traub algorithm (see
382             L).
383             As of 2.086, works with native-complex data.
384              
385             =for usage
386              
387             $roots = polyroots($coeffs); # native complex
388             polyroots($coeffs, $roots=null); # native complex
389             ($rr, $ri) = polyroots($cr, $ci);
390             polyroots($cr, $ci, $rr, $ri);
391             ',);
392              
393             pp_def("polyfromroots",
394             Pars => 'r(m); [o]c(n=CALC($SIZE(m)+1));',
395             GenericTypes => ['CD'],
396             Code => <<'EOF',
397             $c(n=>0) = 1.0;
398             loop(m) %{ $c(n=>m+1) = 0.0; %}
399             PDL_Indx k;
400             loop(m) %{
401             for (k = m; k >= 0; k--) /* count down to use data before we mutate */
402             $c(n=>k+1) -= $r() * $c(n=>k);
403             %}
404             EOF
405             PMCode => pp_line_numbers(__LINE__, <<'EOF'),
406             sub PDL::polyfromroots {
407             my @args = map PDL->topdl($_), @_;
408             my $natcplx = !$args[0]->type->real;
409             barf "need array context" if !$natcplx and !(wantarray//1);
410             if (!$natcplx) {
411             splice @args, 0, 2, $args[0]->czip($args[1]); # r
412             }
413             my @ins = splice @args, 0, 1;
414             my $explicit_out = my @outs = @args;
415             if ($natcplx) {
416             $_ //= PDL->null for $outs[0];
417             } else {
418             $_ //= PDL->null for @outs[0,1];
419             }
420             my @args_out = $natcplx ? @outs : PDL->null;
421             PDL::_polyfromroots_int(@ins, @args_out);
422             if (!$natcplx) {
423             $outs[0] .= $args_out[0]->re;
424             $outs[1] .= $args_out[0]->im;
425             }
426             $natcplx ? $outs[0] : @outs;
427             }
428             EOF
429             Doc => '
430             =for ref
431              
432             Calculates the complex coefficients of a polynomial from its complex
433             roots, in order of decreasing powers. Added in 2.086, works with
434             native-complex data.
435              
436             Algorithm is from Octave poly.m, O(n^2), per
437             L;
438             using an FFT would allow O(n*log(n)^2).
439              
440             =for usage
441              
442             $coeffs = polyfromroots($roots); # native complex
443             ($cr, $ci) = polyfromroots($rr, $ri);
444             ',);
445              
446             pp_def("polyval",
447             Pars => 'c(n); x(); [o]y();',
448             GenericTypes => ['CD'],
449             Code => <<'EOF',
450             $GENERIC(y) vc = $c(n=>0), sc = $x();
451             loop(n=1) %{ vc = vc*sc + $c(); %}
452             $y() = vc;
453             EOF
454             PMCode => pp_line_numbers(__LINE__, <<'EOF'),
455             sub PDL::polyval {
456             my @args = map PDL->topdl($_), @_;
457             my $natcplx = !$args[0]->type->real;
458             barf "need array context" if !$natcplx and !(wantarray//1);
459             if (!$natcplx) {
460             splice @args, 0, 2, $args[0]->czip($args[1]); # c
461             splice @args, 1, 2, $args[1]->czip($args[2]); # x
462             }
463             my @ins = splice @args, 0, 2;
464             my $explicit_out = my @outs = @args;
465             if ($natcplx) {
466             $_ //= PDL->null for $outs[0];
467             } else {
468             $_ //= PDL->null for @outs[0,1];
469             }
470             my @args_out = $natcplx ? @outs : PDL->null;
471             PDL::_polyval_int(@ins, @args_out);
472             if (!$natcplx) {
473             $outs[0] .= $args_out[0]->re;
474             $outs[1] .= $args_out[0]->im;
475             }
476             $natcplx ? $outs[0] : @outs;
477             }
478             EOF
479             Doc => '
480             =for ref
481              
482             Complex value of a complex polynomial at given point, given coefficients
483             in order of decreasing powers. Uses Horner recurrence. Added in 2.086,
484             works with native-complex data.
485              
486             =for usage
487              
488             $y = polyval($coeffs, $x); # native complex
489             ($yr, $yi) = polyval($cr, $ci, $xr, $xi);
490             ',);
491              
492             sub cequiv {
493             my ($func, $ref) = @_;
494             return if !PDL::Core::Dev::got_complex_version($func, 1);
495             pp_def("c$func",
496             GenericTypes => $AF,
497             Pars => 'i(); complex [o] o()',
498             Doc => <
499             =for ref\n
500             Takes real or complex data, returns the complex C<$func>.\n
501             Added in 2.099.
502             EOF
503             Code => pp_line_numbers(__LINE__, <
504             \$TFDEGCH(PDL_CFloat,PDL_CDouble,PDL_CLDouble,PDL_CFloat,PDL_CDouble,PDL_CLDouble) tmp = \$i();
505             tmp = c$func(tmp);
506             \$o() = tmp;
507             EOF
508             );
509             }
510              
511             cequiv($_) for qw(sqrt log acos asin acosh atanh);
512              
513             pp_def('csqrt_up',
514             GenericTypes => $AF,
515             Pars => 'i(); complex [o] o()',
516             Doc => <<'EOF',
517             Take the complex square root of a number choosing that whose imaginary
518             part is not negative, i.e., it is a square root with a branch cut
519             'infinitesimally' below the positive real axis.
520             EOF
521             Code => pp_line_numbers(__LINE__, <<'EOF'),
522 136         625 $TFDEGCH(PDL_CFloat,PDL_CDouble,PDL_CLDouble,PDL_CFloat,PDL_CDouble,PDL_CLDouble) tmp = $i();
523 136         123 tmp = csqrt(tmp);
524 136 50       2622 if (cimag(tmp)<0)
    100          
    100          
    100          
    100          
    50          
525 74         312 tmp = -tmp;
526 136         112 $o() = tmp;
527             EOF
528             );
529              
530             pp_addpm({At=>'Bot'},<<'EOD');
531             =head1 AUTHOR
532              
533             Copyright (C) R.J.R. Williams 1997 (rjrw@ast.leeds.ac.uk), Karl Glazebrook
534             (kgb@aaoepp.aao.gov.au) and Tuomas J. Lukka (Tuomas.Lukka@helsinki.fi).
535             Portions (C) Craig DeForest 2002 (deforest@boulder.swri.edu).
536              
537             All rights reserved. There is no warranty. You are allowed
538             to redistribute this software / documentation under certain
539             conditions. For details, see the file COPYING in the PDL
540             distribution. If this file is separated from the PDL distribution,
541             the PDL copyright notice should be included in the file.
542              
543             =cut
544              
545             EOD
546             pp_done();