=head1 NAME PDL::Interpolate::Slatec - simple interface to SLATEC interpolation routines =head1 SYNOPSIS use PDL::Interpolate::Slatec; use PDL::Math; # somewhat pointless way to estimate cos and sin, # but is shows that you can thread if you want to # my $x = pdl( 0 .. 45 ) * 4 * 3.14159 / 180; my $y = cat( sin($x), cos($x) ); # my $obj = new PDL::Interpolate::Slatec( x => $x, y = $y ); # my $xi = pdl( 0.5, 1.5, 2.5 ); my $yi = $obj->interpolate( $xi ); # print "cos( $xi ) equals ", $yi->slice(':,(0)'), "\n"; cos( [0.5 1.5 2.5] ) equals [0.87759844 0.070737667 -0.80115622] # print "sin( $xi ) equals ", $yi->slice(':,(1)'), "\n"; sin( [0.5 1.5 2.5] ) equals [ 0.4794191 0.99768655 0.59846449] # print cos($xi), "\n", sin($xi), "\n"; [0.87758256 0.070737202 -0.80114362] [0.47942554 0.99749499 0.59847214] =head1 DESCRIPTION Use the interface defined by L to provide a simple way to use the SLATEC interpolation routines (e.g. see L). Hence the name for this library - as returned by the C method - is C<"Slatec">. Currently, only the L are available (C). =head2 Attributes The following changes are made to the attributes of L: Attribute Flag Description bc sgr boundary conditions g g estimated gradient at x positions Attribute Default Allowed values bc "simple" see Boundary conditions section type "pch" Given the initial set of points C<(x,y)>, the C<"pch"> library estimates the gradient at these points using the given boundary conditions (as specified by the C attribute). The estimated gradient can be obtained using $gradient = $obj->get( 'g' ); As described in the L method, the C<"pch"> routines can also estimate the gradient, as well as the function value, for a set of C<$xi>. =head2 Boundary conditions for the pch routines If your data is monotonic, and you are not too bothered about edge effects, then the default value of C of C<"simple"> is for you. Otherwise, take a look at the description of L and use a hash reference for the C attribute, with the following keys: =over 3 =item monotonic 0 if the interpolant is to be monotonic in each interval (so the gradient will be 0 at each switch point), otherwise the gradient is calculated using a 3-point difference formula at switch points. If E 0 then the interpolant is forced to lie close to the data, if E 0 no such control is imposed. Default = B<0>. =item start A perl list of one or two elements. The first element defines how the boundary condition for the start of the array is to be calculated; it has a range of C<-5 .. 5>, as given for the C parameter of L. The second element, only used if options 2, 1, -1, or 2 are chosen, contains the value of the C parameter. Default = B<[ 0 ]>. =item end As for C, but for the end of the data. =back An example would be $obj->set( bc => { start => [ 1, 0 ], end => [ 1, -1 ] } which sets the first derivative at the first point to 0, and at the last point to -1. =head2 Errors The C method provides a simple mechanism to check if the previous method was successful. The C attribute contains the C<$ierr> piddle returned by the Slatec routine if a more precise diagnostic is required. To find out which routine was called, use the C method. =cut package PDL::Interpolate::Slatec; use PDL::Interpolate; use PDL::Slatec; use Carp; use strict; use vars qw( @ISA ); @ISA = qw ( PDL::Interpolate ); #################################################################### # #################################################################### # ## Public routines: sub new { my $this = shift; my $class = ref($this) || $this; my $self = $class->SUPER::new(); # note: do not send in values # change from PDL::Interpolate to PDL::Interpolate::Slatec bless ($self, $class); # change class attributes $self->_change_attr( bc => { required => 1, settable => 1 }, # already gettable ); $self->_set_value( bc => "simple", type => "pch" ); $self->_add_attr( g => { gettable => 1 }, ); $self->{flags}->{library} = "Slatec"; $self->{flags}->{routine} = "none"; # set variables $self->set( @_ ); return $self; } # sub: new #################################################################### # set up the interpolation # sub _initialise { my $self = shift; # set up error flags $self->{flags}->{status} = 0; $self->{flags}->{routine} = "none"; # get values in one go my ( $x, $y, $g, $bc ) = $self->_get_value( qw( x y g bc ) ); # check 1st dimention of x and y are the same # ie allow the possibility of threading my $xdim = $x->getdim( 0 ); my $ydim = $y->getdim( 0 ); croak "ERROR: x and y piddles must have the same first dimension.\n" unless $xdim == $ydim; # if a gradient has been specified, then we don't need to do anything # - other than check the dimensions if ( defined $g ) { croak "ERROR: gradient piddle must have the same first dimension as x and y.\n" unless $g->getdim( 0 ) == $xdim; $self->{flags}->{status} = 1; return; } my $ierr; if ( ref($bc) eq "HASH" ) { my $monotonic = $bc->{monotonic} || 0; my $start = $bc->{start} || [ 0 ]; my $end = $bc->{end} || [ 0 ]; my $ic = $x->short( $start->[0], $end->[0] ); my $vc = $x->float( 0, 0 ); # it will get promoted if required if ( $#$start == 1 ) { $vc->set( 0, $start->[1] ); } if ( $#$end == 1 ) { $vc->set( 1, $end->[1] ); } my $wk = $x->zeroes( $x->float, 2*$xdim ); ( $g, $ierr ) = chic( $ic, $vc, $monotonic, $x, $y, $wk ); $self->{flags}->{routine} = "chic"; } elsif ( $bc eq "simple" ) { # chim ( $g, $ierr ) = chim( $x, $y ); $self->{flags}->{routine} = "chim"; } else { # Unknown boundary condition croak "ERROR: unknown boundary condition <$bc>.\n"; # return; } $self->_set_value( g => $g, err => $ierr ); if ( all $ierr == 0 ) { # everything okay $self->{flags}->{status} = 1; } elsif ( any $ierr < 0 ) { # a problem $self->{flags}->{status} = 0; } else { # there were switches in monotonicity $self->{flags}->{status} = -1; } } # sub: _initialise #################################################################### =head2 interpolate =for usage my $yi = $obj->interpolate( $xi ); my ( $yi, $gi ) = $obj->interpolate( $xi ); =for ref Returns the interpolated function and derivative at a given set of points. If evaluated in scalar mode, it returns only the interpolated function values. =cut sub interpolate { my $self = shift; my $xi = shift; croak 'Usage: $obj->interpolate( $xi )' . "\n" unless defined $xi; # check everything is fine $self->_check_attr(); # get values in one go my ( $x, $y, $g ) = $self->_get_value( qw( x y g ) ); my ( $yi, $gi, $ierr ); if ( wantarray ) { ( $yi, $gi, $ierr ) = chfd( $x, $y, $g, 0, $xi ); $self->{flags}->{routine} = "chfd"; } else { ( $yi, $ierr ) = chfe( $x, $y, $g, 0, $xi ); $self->{flags}->{routine} = "chfe"; } # set err/status info $self->_set_value( err => $ierr ); if ( all $ierr == 0 ) { # everything okay $self->{flags}->{status} = 1; } elsif ( all $ierr > 0 ) { # extrapolation was required $self->{flags}->{status} = -1; } else { # a problem $self->{flags}->{status} = 0; } return wantarray ? ( $yi, $gi ) : $yi; } # sub: interpolate =head2 integrate =for usage my $ans = $obj->integrate( index => pdl( 2, 5 ) ); my $ans = $obj->integrate( x => pdl( 2.3, 4.5 ) ); =for ref Integrate the function stored in the PDL::Interpolate::Slatec object. The integration can either be between points of the original C array (C), or arbitrary x values (C). For both cases, a two element piddle should be given, to specify the start and end points of the integration. =over 7 =item index The values given refer to the indices of the points in the C array. =item x The array contains the actual values to integrate between. =back If the C method returns a value of -1, then one or both of the integration limits did not lie inside the C array. I with the result in such a case. The reason for using piddles, rather than arrays, is that it allows for threading. =cut sub integrate { my $self = shift; croak 'Usage: $obj->integrate( $type => $limits )' . "\n" unless $#_ == 1; # check everything is fine $self->_check_attr(); $self->{flags}->{status} = 0; $self->{flags}->{routine} = "none"; my ( $type, $indices ) = ( @_ ); croak "Unknown type ($type) sent to integrate method.\n" unless $type eq "x" or $type eq "index"; my $fdim = $indices->getdim(0); croak "Indices must have a first dimension of 2, not $fdim.\n" unless $fdim == 2; my $lo = $indices->slice('(0)'); my $hi = $indices->slice('(1)'); my ( $x, $y, $g ) = $self->_get_value( qw( x y g ) ); my ( $ans, $ierr ); if ( $type eq "x" ) { ( $ans, $ierr ) = chia( $x, $y, $g, 0, $lo, $hi ); $self->{flags}->{routine} = "chia"; if ( all $ierr == 0 ) { # everything okay $self->{flags}->{status} = 1; } elsif ( any $ierr < 0 ) { # a problem $self->{flags}->{status} = 0; } else { # out of range $self->{flags}->{status} = -1; } } else { ( $ans, $ierr ) = chid( $x, $y, $g, 0, $lo, $hi ); $self->{flags}->{routine} = "chid"; if ( all $ierr == 0 ) { # everything okay $self->{flags}->{status} = 1; } elsif ( all $ierr != -4 ) { # a problem $self->{flags}->{status} = 0; } else { # out of range (ierr == -4) $self->{flags}->{status} = -1; } } $self->_set_value( err => $ierr ); return $ans; } =head1 AUTHOR Copyright (C) 2000 Doug Burke (burke@ifa.hawaii.edu). All rights reserved. There is no warranty. You are allowed to redistribute this software / documentation as described in the file COPYING in the PDL distribution. =head1 SEE ALSO L, L, perltoot(1). =cut #################################################################### # End with a true 1;