File Coverage

blib/lib/PDL/Func.pm
Criterion Covered Total %
statement 108 216 50.0
branch 28 116 24.1
condition 3 18 16.6
subroutine 17 22 77.2
pod 10 10 100.0
total 166 382 43.4


line stmt bran cond sub pod time code
1             =encoding iso-8859-1
2              
3             =head1 NAME
4              
5             PDL::Func - interpolation, integration, & gradient estimation (differentiation) of functions
6              
7             =head1 SYNOPSIS
8              
9             use PDL::Func;
10             use PDL::Math;
11              
12             # somewhat pointless way to estimate cos and sin,
13             # but is shows that you can thread if you want to
14             # (and the library lets you)
15             #
16             my $obj = PDL::Func->init( Interpolate => "Hermite" );
17             #
18             my $x = pdl( 0 .. 45 ) * 4 * 3.14159 / 180;
19             my $y = cat( sin($x), cos($x) );
20             $obj->set( x => $x, y => $y, bc => "simple" );
21             #
22             my $xi = pdl( 0.5, 1.5, 2.5 );
23             my $yi = $obj->interpolate( $xi );
24             #
25             print "sin( $xi ) equals ", $yi->slice(':,(0)'), "\n";
26             sin( [0.5 1.5 2.5] ) equals [0.87759844 0.070737667 -0.80115622]
27             #
28             print "cos( $xi ) equals ", $yi->slice(':,(1)'), "\n";
29             cos( [0.5 1.5 2.5] ) equals [ 0.4794191 0.99768655 0.59846449]
30             #
31             print sin($xi), "\n", cos($xi), "\n";
32             [0.47942554 0.99749499 0.59847214]
33             [0.87758256 0.070737202 -0.80114362]
34              
35             =head1 DESCRIPTION
36              
37             This module aims to contain useful functions. Honest.
38              
39             =head1 INTERPOLATION AND MORE
40              
41             This module aims to provide a relatively-uniform interface
42             to the various interpolation methods available to PDL.
43             The idea is that a different interpolation scheme
44             can be used just by changing an attribute of a C
45             object.
46             Some interpolation schemes (as exemplified by the SLATEC
47             library) also provide additional functionality, such as
48             integration and gradient estimation.
49              
50             Throughout this documentation, C<$x> and C<$y> refer to the function
51             to be interpolated whilst C<$xi> and C<$yi> are the interpolated values.
52              
53             The available types, or I, of interpolation are listed below.
54             Also given are the valid attributes for each scheme: the flag value
55             indicates whether it can be set (s), got (g), and if it is
56             required (r) for the method to work.
57              
58             =over 4
59              
60             =item Interpolate => Linear
61              
62             An extravagent way of calling the linear interpolation routine
63             L.
64              
65             The valid attributes are:
66              
67             Attribute Flag Description
68             x sgr x positions of data
69             y sgr function values at x positions
70             err g error flag
71              
72             =item Interpolate => Hermite
73              
74             Use the piecewice cubic Hermite interpolation routines
75             from the SLATEC library.
76             Only available if L is installed.
77              
78             The valid attributes are:
79              
80             Attribute Flag Description
81             x sgr x positions of data
82             y sgr function values at x positions
83             bc sgr boundary conditions
84             g g estimated gradient at x positions
85             err g error flag
86              
87             Given the initial set of points C<(x,y)>, an estimate of the
88             gradient is made at these points, using the given boundary
89             conditions. The gradients are stored in the C attribute,
90             accessible via:
91              
92             $gradient = $obj->get( 'g' );
93              
94             However, as this gradient is only calculated 'at the last moment',
95             C will only contain data I one of
96             C, C, or C is used.
97              
98             =back
99              
100             =head2 Boundary conditions for the Hermite routines
101              
102             If your data is monotonic, and you are not too bothered about
103             edge effects, then the default value of C of C is for you.
104             Otherwise, take a look at the description of
105             L and use a hash reference
106             for the C attribute, with the following keys:
107              
108             =over 3
109              
110             =item monotonic
111              
112             0 if the interpolant is to be monotonic in each interval (so
113             the gradient will be 0 at each switch point),
114             otherwise the gradient is calculated using a 3-point difference
115             formula at switch points.
116             If E 0 then the interpolant is forced to lie close to the
117             data, if E 0 no such control is imposed.
118             Default = B<0>.
119              
120             =item start
121              
122             A perl list of one or two elements. The first element defines how the
123             boundary condition for the start of the array is to be calculated;
124             it has a range of C<-5 .. 5>, as given for the C parameter
125             of L.
126             The second element, only used if options 2, 1, -1, or 2
127             are chosen, contains the value of the C parameter.
128             Default = B<[ 0 ]>.
129              
130             =item end
131              
132             As for C, but for the end of the data.
133              
134             =back
135              
136             An example would be
137              
138             $obj->set( bc => { start => [ 1, 0 ], end => [ 1, -1 ] } )
139              
140             which sets the first derivative at the first point to 0,
141             and at the last point to -1.
142              
143             =head2 Errors
144              
145             The C method provides a simple mechanism to check if
146             the previous method was successful.
147             If the function returns an error flag, then it is stored
148             in the C attribute.
149             To find out which routine was used, use the
150             C method.
151              
152             =cut
153              
154             #' fool emacs
155              
156             package PDL::Func;
157              
158 1     1   906 use strict;
  1         3  
  1         29  
159 1     1   5 use Carp;
  1         2  
  1         84  
160              
161             ####################################################################
162             #
163             # what modules are available ?
164             #
165             my %modules;
166             BEGIN {
167 1     1   70 eval "use PDL::Slatec";
  1     1   162  
  0         0  
  0         0  
168 1 50       2866 $modules{slatec} = ($@ ? 0 : 1);
169             }
170              
171             ####################################################################
172            
173             ## Public routines:
174              
175             =head1 FUNCTIONS
176              
177             =head2 init
178              
179             =for usage
180              
181             $obj = PDL::Func->init( Interpolate => "Hermite", x => $x, y => $y );
182             $obj = PDL::Func->init( { x => $x, y => $y } );
183              
184             =for ref
185              
186             Create a PDL::Func object, which can interpolate, and possibly
187             integrate and calculate gradients of a dataset.
188              
189             If not specified, the value of Interpolate is taken to be
190             C, which means the interpolation is performed by
191             L.
192             A value of C uses piecewise cubic Hermite functions,
193             which also allows the integral and gradient of the data
194             to be estimated.
195              
196             Options can either be provided directly to the method, as in the
197             first example, or within a hash reference, as shown in the second
198             example.
199              
200             =cut
201              
202             # meaning of types:
203             # required - required, if this attr is changed, we need to re-initialise
204             # settable - can be changed with a init() or set() command
205             # gettable - can be read with a get() command
206             #
207             # do we really need gettable? Not currently, that's for sure,
208             # as everything is gettable
209              
210             my %attr =
211             (
212             Default => {
213             x => { required => 1, settable => 1, gettable => 1 },
214             y => { required => 1, settable => 1, gettable => 1 },
215             err => { gettable => 1 },
216             },
217             Linear => {},
218             Hermite => {
219             bc => { settable => 1, gettable => 1, required => 1, default => "simple" },
220             g => { gettable => 1 },
221             },
222             );
223              
224             sub init {
225 1     1 1 337 my $this = shift;
226 1   33     9 my $class = ref($this) || $this;
227              
228             # class structure
229 1         2 my $self = { };
230              
231             # make $self into an object
232 1         3 bless $self, $class;
233              
234             # set up default attributes
235             #
236 1         5 my ( %opt ) = @_;
237 1 50       5 $opt{Interpolate} = "Linear" unless exists $opt{Interpolate};
238              
239             # set variables
240 1         7 $self->set( %opt );
241            
242             # return the object
243 1         5 return $self;
244            
245             } # sub: init()
246              
247             #####################################################################
248              
249             # $self->_init_attr( $interpolate )
250             #
251             # set up the object for the given interpolation method
252             # - uses the values stored in %attr to fill in the
253             # fields in $self AFTER clearing the object
254             #
255             # NOTE: called by set()
256             #
257             sub _init_attr {
258 1     1   2 my $self = shift;
259 1         2 my $interpolate = shift;
260              
261             croak "ERROR: Unknown interpolation scheme <$interpolate>.\n"
262 1 50       4 unless defined $attr{$interpolate};
263              
264             # fall over if slatec library isn't present
265             # and asking for Hermite interpolation
266             croak "ERROR: Hermite interpolation is not available without PDL::Slatec.\n"
267 1 50 33     5 if $interpolate eq "Interpolate" and $modules{slatec} == 0;
268              
269             # clear out the old data (if it's not the first time through)
270 1         6 $self->{attributes} = {};
271 1         117 $self->{values} = {};
272 1         75 $self->{types} = { required => 0, settable => 0, gettable => 0 };
273 1         9 $self->{flags} = { scheme => $interpolate, status => 1, routine => "none", changed => 1 };
274              
275             # set up default values
276 1         3 my $ref = $attr{Default};
277 1         3 foreach my $attr ( keys %{$ref} ) {
  1         5  
278             # set default values
279 3         4 foreach my $type ( keys %{$self->{types}} ) {
  3         8  
280 9         18 $self->{attributes}{$attr}{$type} = $self->{types}{$type};
281             }
282              
283             # change the values to those supplied
284 3         5 foreach my $type ( keys %{$ref->{$attr}} ) {
  3         8  
285             $self->{attributes}{$attr}{$type} = $ref->{$attr}{$type}
286 7 50       16 if exists $self->{types}{$type};
287             }
288             # set value to undef
289 3         7 $self->{values}{$attr} = undef;
290             }
291              
292             # now set up for the particular interpolation scheme
293 1         3 $ref = $attr{$interpolate};
294 1         2 foreach my $attr ( keys %{$ref} ) {
  1         4  
295             # set default values, if not known
296 0 0       0 unless ( defined $self->{attributes}{$attr} ) {
297 0         0 foreach my $type ( keys %{$self->{types}} ) {
  0         0  
298 0         0 $self->{attributes}{$attr}{$type} = $self->{types}{$type};
299             }
300             }
301              
302             # change the values to those supplied
303 0         0 foreach my $type ( keys %{$ref->{$attr}} ) {
  0         0  
304 0 0       0 next if $type eq "default";
305             $self->{attributes}{$attr}{$type} = $ref->{$attr}{$type}
306 0 0       0 if exists $self->{types}{$type};
307             }
308             # set value to default value/undef
309             $self->{values}{$attr} =
310 0 0       0 exists $ref->{$attr}{default} ? $ref->{$attr}{default} : undef;
311             }
312             } # sub: _init_attr()
313              
314             ####################################################################
315              
316             # call this at the start of each method that needs data
317             # stored in the object. This function ensures that all required
318             # attributes exist and, if necessary, re-initialises the object
319             # - ie if the data has changed.
320             #
321             sub _check_attr {
322 1     1   2 my $self = shift;
323 1 50       4 return unless $self->{flags}{changed};
324              
325 1         2 my @emsg;
326 1         2 foreach my $name ( keys %{ $self->{attributes} } ) {
  1         6  
327 3 100       9 if( $self->{attributes}{$name}{required} ) {
328 2 50       6 push @emsg, $name unless defined($self->{values}{$name});
329             }
330             }
331 1 50       4 croak "ERROR - the following attributes must be supplied:\n [ @emsg ]\n"
332             unless $#emsg == -1;
333            
334 1         3 $self->{flags}{routine} = "none";
335 1         2 $self->{flags}{status} = 1;
336            
337 1         3 $self->_initialise;
338 1         2 $self->{flags}{changed} = 0;
339              
340             } # sub: _check_attr()
341              
342             ####################################################################
343              
344             # for a given scheme, it may be necessary to perform certain
345             # operations before the main routine of a method is called.
346             # It's done here.
347             #
348             # Due to lazy evaluation we try to do this as late as possible -
349             # _initialise() should only be called by _check_attr()
350             # [ at least at the moment ]
351             #
352             sub _initialise {
353 1     1   2 my $self = shift;
354              
355 1         3 my $iflag = $self->scheme();
356 1 50       5 if ( $iflag eq "Hermite" ) {
357 0         0 _init_hermite( $self );
358             }
359            
360             } # sub: _initialise()
361              
362             # something has changed, so we need to recalculate the gradient
363             # - actually, some changes don't invalidate the gradient,
364             # however, with the current design, it's impossible to know
365             # this. (poor design)
366             #
367             sub _init_hermite {
368 0     0   0 my $self = shift;
369              
370             # set up error flags
371 0         0 $self->{flags}{status} = 0;
372 0         0 $self->{flags}{routine} = "none";
373              
374             # get values in one go
375 0         0 my ( $x, $y, $bc ) = $self->_get_value( qw( x y bc ) );
376              
377             # check 1st dimention of x and y are the same
378             # ie allow the possibility of threading
379 0         0 my $xdim = $x->getdim( 0 );
380 0         0 my $ydim = $y->getdim( 0 );
381 0 0       0 croak "ERROR: x and y piddles must have the same first dimension.\n"
382             unless $xdim == $ydim;
383              
384 0         0 my ( $g, $ierr );
385 0 0       0 if ( ref($bc) eq "HASH" ) {
    0          
386 0   0     0 my $monotonic = $bc->{monotonic} || 0;
387 0   0     0 my $start = $bc->{start} || [ 0 ];
388 0   0     0 my $end = $bc->{end} || [ 0 ];
389              
390 0         0 my $ic = $x->short( $start->[0], $end->[0] );
391 0         0 my $vc = $x->float( 0, 0 );
392              
393 0 0       0 if ( $#$start == 1 ) { $vc->set( 0, $start->[1] ); }
  0         0  
394 0 0       0 if ( $#$end == 1 ) { $vc->set( 1, $end->[1] ); }
  0         0  
395              
396 0         0 my $wk = $x->zeroes( $x->float, 2*$xdim );
397             croak "ERROR: Hermite interpolation is not available without PDL::Slatec.\n"
398 0 0       0 if $modules{slatec} == 0;
399 0         0 ( $g, $ierr ) = chic( $ic, $vc, $monotonic, $x, $y, $wk );
400              
401 0         0 $self->{flags}{routine} = "chic";
402              
403             } elsif ( $bc eq "simple" ) {
404             # chim
405             croak "ERROR: Hermite interpolation is not available without PDL::Slatec.\n"
406 0 0       0 if $modules{slatec} == 0;
407 0         0 ( $g, $ierr ) = chim( $x, $y );
408              
409 0         0 $self->{flags}{routine} = "chim";
410              
411             } else {
412             # Unknown boundary condition
413 0         0 croak "ERROR: unknown boundary condition <$bc>.\n";
414             # return;
415             }
416              
417 0         0 $self->_set_value( g => $g, err => $ierr );
418              
419 0 0       0 if ( all $ierr == 0 ) {
    0          
420             # everything okay
421 0         0 $self->{flags}{status} = 1;
422             } elsif ( any $ierr < 0 ) {
423             # a problem
424 0         0 $self->{flags}{status} = 0;
425             } else {
426             # there were switches in monotonicity
427 0         0 $self->{flags}{status} = -1;
428             }
429              
430              
431             }
432              
433             ####################################################################
434             ####################################################################
435              
436             # a version of set that ignores the settable flag
437             # and doesn't bother about the presence of an Interpolate
438             # value.
439             #
440             # - for use by the class, not by the public
441             #
442             # it still ignores unknown attributes
443             #
444             sub _set_value {
445 1     1   2 my $self = shift;
446 1         4 my %attrs = ( @_ );
447            
448 1         5 foreach my $attr ( keys %attrs ) {
449 1 50       4 if ( exists($self->{values}{$attr}) ) {
450 1         2 $self->{values}{$attr} = $attrs{$attr};
451 1         3 $self->{flags}{changed} = 1;
452             }
453             }
454              
455             } # sub: _set_value()
456              
457             # a version of get that ignores the gettable flag
458             # - for use by the class, not by the public
459             #
460             # an unknown attribute returns an undef
461             #
462             sub _get_value {
463 1     1   2 my $self = shift;
464              
465 1         1 my @ret;
466 1         2 foreach my $name ( @_ ) {
467 2 50       6 if ( exists $self->{values}{$name} ) {
468 2         6 push @ret, $self->{values}{$name};
469             } else {
470 0         0 push @ret, undef;
471             }
472             }
473              
474 1 50       5 return wantarray ? @ret : $ret[0];
475              
476             } # sub: _get_value()
477              
478             ####################################################################
479              
480             =head2 set
481              
482             =for usage
483              
484             my $nset = $obj->set( x => $newx, y => $newy );
485             my $nset = $obj->set( { x => $newx, y => $newy } );
486              
487             =for ref
488              
489             Set attributes for a PDL::Func object.
490              
491             The return value gives the number of the supplied attributes
492             which were actually set.
493              
494             =cut
495              
496             sub set {
497 1     1 1 2 my $self = shift;
498 1 50       4 return if $#_ == -1;
499              
500 1         3 my $vref;
501 1 50 33     6 if ( $#_ == 0 and ref($_[0]) eq "HASH" ) {
502 0         0 $vref = shift;
503             } else {
504 1         3 my %vals = ( @_ );
505 1         3 $vref = \%vals;
506             }
507              
508             # initialise attributes IFF Interpolate
509             # is specified
510             #
511             $self->_init_attr( $vref->{Interpolate} )
512 1 50       8 if exists $vref->{Interpolate};
513              
514 1         2 my $ctr = 0;
515 1         1 foreach my $name ( keys %{$vref} ) {
  1         4  
516 3 100       8 next if $name eq "Interpolate";
517 2 50       6 if ( exists $self->{attributes}{$name}{settable} ) {
518 2         3 $self->{values}{$name} = $vref->{$name};
519 2         4 $ctr++;
520             }
521             }
522              
523 1 50       4 $self->{flags}{changed} = 1 if $ctr;
524 1         2 $self->{flags}{status} = 1;
525 1         3 return $ctr;
526              
527             } # sub: set()
528              
529             ####################################################################
530              
531             =head2 get
532              
533             =for usage
534              
535             my $x = $obj->get( x );
536             my ( $x, $y ) = $obj->get( qw( x y ) );
537              
538             =for ref
539              
540             Get attributes from a PDL::Func object.
541              
542             Given a list of attribute names, return a list of
543             their values; in scalar mode return a scalar value.
544             If the supplied list contains an unknown attribute,
545             C returns a value of C for that
546             attribute.
547              
548             =cut
549              
550             sub get {
551 1     1 1 3 my $self = shift;
552              
553 1         2 my @ret;
554 1         3 foreach my $name ( @_ ) {
555 1 50       5 if ( exists $self->{attributes}{$name}{gettable} ) {
556 1         3 push @ret, $self->{values}{$name};
557             } else {
558 0         0 push @ret, undef;
559             }
560             }
561              
562 1 50       4 return wantarray ? @ret : $ret[0];
563              
564             } # sub: get()
565              
566             ####################################################################
567             #
568             # access to flags - have individual methods for these
569              
570             =head2 scheme
571              
572             =for usage
573              
574             my $scheme = $obj->scheme;
575              
576             =for ref
577              
578             Return the type of interpolation of a PDL::Func object.
579              
580             Returns either C or C.
581              
582             =cut
583              
584 4     4 1 177 sub scheme { return $_[0]->{flags}{scheme}; }
585              
586             =head2 status
587              
588             =for usage
589              
590             my $status = $obj->status;
591              
592             =for ref
593              
594             Returns the status of a PDL::Func object.
595              
596             This method provides a high-level indication of
597             the success of the last method called
598             (except for C which is ignored).
599             Returns B<1> if everything is okay, B<0> if
600             there has been a serious error,
601             and B<-1> if there
602             was a problem which was not serious.
603             In the latter case, C<$obj-Eget("err")> may
604             provide more information, depending on the
605             particular scheme in use.
606              
607             =cut
608              
609 1     1 1 8 sub status { return $_[0]->{flags}{status}; }
610              
611             =head2 routine
612              
613             =for usage
614              
615             my $name = $obj->routine;
616              
617             =for ref
618              
619             Returns the name of the last routine called by a PDL::Func object.
620              
621             This is mainly useful for decoding the value stored in the
622             C attribute.
623              
624             =cut
625              
626 0     0 1 0 sub routine { return $_[0]->{flags}{routine}; }
627              
628             =head2 attributes
629              
630             =for usage
631              
632             $obj->attributes;
633             PDL::Func->attributes;
634              
635             =for ref
636              
637             Print out the flags for the attributes of a PDL::Func object.
638              
639             Useful in case the documentation is just too opaque!
640              
641             =for example
642              
643             PDL::Func->attributes;
644             Flags Attribute
645             SGR x
646             SGR y
647             G err
648              
649             =cut
650              
651             # note, can be called with the class, rather than just
652             # an object. However, not of great use, as this will only
653             # ever return the values for Interpolate => Linear
654             #
655             # to allow this, I've used a horrible hack - we actually
656             # create an object and then print out the attributes from that
657             # Ugh!
658             #
659             # It would have been useful if I'd stuck to sub-classes
660             # for different schemes
661             #
662             sub attributes {
663 0     0 1 0 my $self = shift;
664              
665             # ugh
666 0 0       0 $self = $self->init unless ref($self);
667              
668 0         0 print "Flags Attribute\n";
669 0         0 while ( my ( $attr, $hashref ) = each %{$self->{attributes}} ) {
  0         0  
670 0         0 my $flag = "";
671 0 0       0 $flag .= "S" if $hashref->{settable};
672 0 0       0 $flag .= "G" if $hashref->{gettable};
673 0 0       0 $flag .= "R" if $hashref->{required};
674            
675 0         0 printf " %-3s %s\n", $flag, $attr;
676             }
677 0         0 return;
678             } # sub: attributes()
679              
680             ####################################################################
681              
682             =head2 interpolate
683              
684             =for usage
685              
686             my $yi = $obj->interpolate( $xi );
687              
688             =for ref
689              
690             Returns the interpolated function at a given set of points
691             (PDL::Func).
692              
693             A status value of -1, as returned by the C method,
694             means that some of the C<$xi> points lay outside the
695             range of the data. The values for these points
696             were calculated by extrapolation (the details depend on the
697             scheme being used).
698              
699             =cut
700              
701             sub interpolate {
702 1     1 1 3 my $self = shift;
703 1         2 my $xi = shift;
704              
705 1 50       4 croak 'Usage: $obj->interpolate( $xi )' . "\n"
706             unless defined $xi;
707              
708             # check everything is fine
709 1         4 $self->_check_attr();
710              
711             # get values in one go
712 1         4 my ( $x, $y ) = $self->_get_value( qw( x y ) );
713              
714             # farm off to routines
715 1         3 my $iflag = $self->scheme;
716 1 50       5 if ( $iflag eq "Linear" ) {
    0          
717 1         4 return _interp_linear( $self, $xi, $x, $y );
718             } elsif ( $iflag eq "Hermite" ) {
719 0         0 return _interp_hermite( $self, $xi, $x, $y );
720             }
721              
722             } # sub: interpolate()
723              
724             sub _interp_linear {
725 1     1   3 my ( $self, $xi, $x, $y ) = ( @_ );
726              
727 1         52 my ( $yi, $err ) = PDL::Primitive::interpolate( $xi, $x, $y );
728              
729 1 50       10 $self->{flags}{status} = (any $err) ? -1 : 1;
730 1         6 $self->_set_value( err => $err );
731 1         3 $self->{flags}{routine} = "interpolate";
732              
733 1         3 return $yi;
734             } # sub: _interp_linear()
735              
736             sub _interp_hermite {
737 0     0   0 my ( $self, $xi, $x, $y ) = ( @_ );
738              
739             # get gradient
740 0         0 my $g = $self->_get_value( 'g' );
741              
742 0         0 my ( $yi, $ierr ) = chfe( $x, $y, $g, 0, $xi );
743 0         0 $self->{flags}{routine} = "chfe";
744 0         0 $self->_set_value( err => $ierr );
745              
746 0 0       0 if ( all $ierr == 0 ) {
    0          
747             # everything okay
748 0         0 $self->{flags}{status} = 1;
749             } elsif ( all $ierr > 0 ) {
750             # extrapolation was required
751 0         0 $self->{flags}{status} = -1;
752             } else {
753             # a problem
754 0         0 $self->{flags}{status} = 0;
755             }
756            
757 0         0 return $yi;
758             } # sub: _interp_linear()
759              
760             =head2 gradient
761              
762             =for usage
763              
764             my $gi = $obj->gradient( $xi );
765             my ( $yi, $gi ) = $obj->gradient( $xi );
766              
767             =for ref
768              
769             Returns the derivative and, optionally,
770             the interpolated function for the C
771             scheme (PDL::Func).
772              
773             =cut
774              
775             sub gradient {
776 1     1 1 2 my $self = shift;
777 1         2 my $xi = shift;
778              
779 1 50       4 croak 'Usage: $obj->gradient( $xi )' . "\n"
780             unless defined $xi;
781              
782 1 50       2 croak 'Error: can not call gradient for Interpolate => "Linear".' ."\n"
783             unless $self->scheme eq "Hermite";
784              
785             # check everything is fine
786 0           $self->_check_attr();
787              
788             # get values in one go
789 0           my ( $x, $y, $g ) = $self->_get_value( qw( x y g ) );
790              
791 0           my ( $yi, $gi, $ierr ) = chfd( $x, $y, $g, 0, $xi );
792 0           $self->{flags}{routine} = "chfd";
793 0           $self->_set_value( err => $ierr );
794              
795 0 0         if ( all $ierr == 0 ) {
    0          
796             # everything okay
797 0           $self->{flags}{status} = 1;
798             } elsif ( all $ierr > 0 ) {
799             # extrapolation was required
800 0           $self->{flags}{status} = -1;
801             } else {
802             # a problem
803 0           $self->{flags}{status} = 0;
804             }
805            
806             # note order of values
807 0 0         return wantarray ? ( $yi, $gi ) : $gi;
808              
809             } # sub: gradient
810              
811             =head2 integrate
812              
813             =for usage
814              
815             my $ans = $obj->integrate( index => pdl( 2, 5 ) );
816             my $ans = $obj->integrate( x => pdl( 2.3, 4.5 ) );
817              
818             =for ref
819              
820             Integrate the function stored in the PDL::Func
821             object, if the scheme is C.
822              
823             The integration can either be between points of
824             the original C array (C), or arbitrary x values
825             (C). For both cases, a two element piddle
826             should be given,
827             to specify the start and end points of the integration.
828              
829             =over 7
830              
831             =item index
832              
833             The values given refer to the indices of the points
834             in the C array.
835              
836             =item x
837              
838             The array contains the actual values to integrate between.
839              
840             =back
841              
842             If the C method returns a value of -1, then
843             one or both of the integration limits did not
844             lie inside the C array. I with the
845             result in such a case.
846              
847             =cut
848              
849             sub integrate {
850 0     0 1   my $self = shift;
851              
852 0 0         croak 'Usage: $obj->integrate( $type => $limits )' . "\n"
853             unless $#_ == 1;
854              
855             croak 'Error: can not call integrate for Interpolate => "Linear".' ."\n"
856 0 0         unless $self->{flags}{scheme} eq "Hermite";
857              
858             # check everything is fine
859 0           $self->_check_attr();
860              
861 0           $self->{flags}{status} = 0;
862 0           $self->{flags}{routine} = "none";
863              
864 0           my ( $type, $indices ) = ( @_ );
865              
866 0 0 0       croak "Unknown type ($type) sent to integrate method.\n"
867             unless $type eq "x" or $type eq "index";
868              
869 0           my $fdim = $indices->getdim(0);
870 0 0         croak "Indices must have a first dimension of 2, not $fdim.\n"
871             unless $fdim == 2;
872              
873 0           my $lo = $indices->slice('(0)');
874 0           my $hi = $indices->slice('(1)');
875              
876 0           my ( $x, $y, $g ) = $self->_get_value( qw( x y g ) );
877 0           my ( $ans, $ierr );
878              
879 0 0         if ( $type eq "x" ) {
880 0           ( $ans, $ierr ) = chia( $x, $y, $g, 0, $lo, $hi );
881 0           $self->{flags}{routine} = "chia";
882              
883 0 0         if ( all $ierr == 0 ) {
    0          
884             # everything okay
885 0           $self->{flags}{status} = 1;
886             } elsif ( any $ierr < 0 ) {
887             # a problem
888 0           $self->{flags}{status} = 0;
889             } else {
890             # out of range
891 0           $self->{flags}->{status} = -1;
892             }
893              
894             } else {
895 0           ( $ans, $ierr ) = chid( $x, $y, $g, 0, $lo, $hi );
896 0           $self->{flags}->{routine} = "chid";
897              
898 0 0         if ( all $ierr == 0 ) {
    0          
899             # everything okay
900 0           $self->{flags}{status} = 1;
901             } elsif ( all $ierr != -4 ) {
902             # a problem
903 0           $self->{flags}{status} = 0;
904             } else {
905             # out of range (ierr == -4)
906 0           $self->{flags}{status} = -1;
907             }
908              
909             }
910              
911 0           $self->_set_value( err => $ierr );
912 0           return $ans;
913              
914             } # sub: integrate()
915              
916             ####################################################################
917              
918             =head1 TODO
919              
920             It should be relatively easy to provide an interface to other
921             interpolation routines, such as those provided by the
922             Gnu Scientific Library (GSL), or the B-spline routines
923             in the SLATEC library.
924              
925             In the documentation, the methods are preceded by C
926             to avoid clashes with functions such as C when using
927             the C or C commands within I or I.
928              
929             =head1 HISTORY
930              
931             Amalgamated C and C
932             to form C. Comments greatly appreciated on the
933             current implementation, as it is not too sensible.
934              
935             Thanks to Robin Williams, Halldór Olafsson, and Vince McIntyre.
936              
937             =head1 AUTHOR
938              
939             Copyright (C) 2000,2001 Doug Burke (dburke@cfa.harvard.edu).
940             All rights reserved. There is no warranty.
941             You are allowed to redistribute this software / documentation as
942             described in the file COPYING in the PDL distribution.
943              
944             =cut
945              
946             ####################################################################
947             # End with a true
948             1;
949