=head1 NAME
PDL::Interpolate - provide a consistent interface to the interpolation routines available in PDL
=head1 SYNOPSIS
use PDL::Interpolate;
my $i = new PDL::Interpolate( x => $x, y = $y );
my $y = $i->interpolate( $xi );
=head1 DESCRIPTION
This module aims to provide a relatively-uniform interface
to the various interpolation methods available to PDL.
The idea is that a different interpolation scheme
can be used just by changing the C<new> call.
At present, PDL::Interpolate itself just provides
a somewhat-convoluted interface to the C<interpolate>
function of L<PDL::Primitive|PDL::Primitive/interpolate>.
However, it is expected that derived classes,
such as
L<PDL::Interpolate::Slatec|PDL::Interpolate::Slatec>,
will actually be used in real-world situations.
To use, create a PDL::Interpolate (or a derived class)
object, supplying it with its required attributes.
=head1 LIBRARIES
Currently, the avaliable classes are
=over 4
=item PDL::Interpolate
Provides an interface to the interpolation routines of PDL.
At present this is the linear interpolation routine
L<PDL::Primitive::interpol|PDL::Primitive/interpol>.
=item PDL::Interpolate::Slatec
The SLATEC library contains several approaches to interpolation:
piecewise cubic Hermite functions and B-splines.
At present, only the former method is available.
=back
It should be relatively easy to provide an interface to other
interpolation routines, such as those provided by the
Gnu Scientific Library (GSL).
=head1 ATTRIBUTES
The attributes (or options) of an object are as follows;
derived classes may modify this list.
Attribute Flag Description
x sgr x positions of data
y sgr function values at x positions
bc g boundary conditions
err g error flag
type g type of interpolation
A flag of C<s> means that a user can set this attribute
with the L<new|/new> or L<set|/set> methods,
a flag of C<g> means that the user can obtain the
value of this attribute using L<get|/get>,
and a flag of C<r> means that the attribute is required
when an object is created (see the L<new|/new> method).
Attribute Default value
bc "none"
type "linear"
If a routine is sent an attribute it does not understand, then
it ignores that attribute, except for L<get|/get>, which
returns C<undef> for that value.
=head1 METHODS
The default methods are described below. However, defined classes
may extend them as they see fit, and add new methods.
Throughout this documentation, C<$x> and C<$y> refer to the function
to be interpolated whilst C<$xi> and C<$yi> are the interpolated values.
=head1 THREADING
The class will thread properly if the routines it calls do so.
See the SYNOPSIS section of L<PDL::Interpolate::Slatec>
(if available) for an example.
=cut
use Carp;
use strict;
####################################################################
## Public routines:
=head2 new
=for usage
$obj = new PDL::Interpolate( x => $x, y => $y );
=for ref
Create a PDL::Interpolate object.
The required L<attributes|/attributes> are
C<x> and C<y>.
At present the only available interpolation method
is C<"linear"> - which just uses
L<PDL::Primitive::interpolate|PDL::Primitive::interpolate> - and
there are no options for boundary conditions, which is why
the C<type> and C<bc> attributes can not be changed.
=cut
# meaning of types:
# required - required, if this attr is changed, we need to re-initialise
# settable - can be changed with a new() or set() command
# gettable - can be read with a get() command
#
sub new {
my $this = shift;
my $class = ref($this) || $this;
# class structure
my $self = {
attributes => {},
values => {},
types => { required => 0, settable => 0, gettable => 0 },
flags => { library => "PDL", status => 1, routine => "none", changed => 1 },
};
# make $self into an object
bless $self, $class;
# set up default attributes
#
$self->_add_attr(
x => { required => 1, settable => 1, gettable => 1 },
y => { required => 1, settable => 1, gettable => 1 },
bc => { gettable => 1 },
err => { gettable => 1 },
type => { gettable => 1 },
);
$self->_set_value(
bc => "none",
type => "linear",
);
# set variables
# - expect sub-classes to call this new with no variables, so $#_ == -1
$self->set( @_ ) if ( @_ );
# return the object
return $self;
} # sub: new()
#####################################################################
# in _add_attr(), _change_attr() and _add_attr_type()
# we set flags->changed to 1 when something changes. It's
# a bit over the top to do this, as these should only be called when
# creating the object, when the changed flag should be set to 1 anyway
# add attributes to the object and sets value to undef
#
# supply a hash array, keys == variable name,
# values are a hash array with keys matching
# $self->{values}, which also gives the default value
# for the type
#
# this can only be used to create an attribute -
# see _change_attr() to change an already exsiting attribute.
#
# the fields are set to the default values, then filled in with the supplied values
# any value that is unknown is ignored
#
sub _add_attr {
my $self = shift;
my %attrs = ( @_ );
foreach my $attr ( keys %attrs ) {
croak "ERROR: adding an attribute ($attr) which is already known.\n"
if defined $self->{attributes}->{$attr};
# set default values
foreach my $type ( keys %{$self->{types}} ) {
$self->{attributes}->{$attr}->{$type} = $self->{types}->{$type};
}
# change the values to those supplied
foreach my $type ( keys %{$attrs{$attr}} ) {
$self->{attributes}->{$attr}->{$type} = $attrs{$attr}->{$type}
if exists $self->{types}->{$type};
}
# set value to undef
$self->{values}->{$attr} = undef;
}
$self->{flags}->{changed} = 1;
} # sub: _add_attr()
# changes attributes of the object
#
# the given attributes MUST already exist
#
sub _change_attr {
my $self = shift;
my %attrs = ( @_ );
foreach my $attr ( keys %attrs ) {
croak "ERROR: changing an attribute ($attr) which is not known.\n"
unless defined $self->{attributes}->{$attr};
# change the values to those supplied
foreach my $type ( keys %{$attrs{$attr}} ) {
if ( exists $self->{types}->{$type} ) {
$self->{attributes}->{$attr}->{$type} = $attrs{$attr}->{$type};
$self->{flags}->{changed} = 1;
}
}
}
} # sub: _change_attr()
# adds the given types to the allowed list, and
# updates all attributes to contain the default value
#
# Useful for sub-classes which add new types
#
sub _add_attr_type {
my $self = shift;
my %types = ( @_ );
foreach my $type ( keys %types ) {
croak "ERROR: adding type ($type) that is already known.\n"
if exists $self->{types}->{$type};
$self->{types}->{$type} = $types{$type};
# loop through each attribute, adding this type
foreach my $attr ( keys %{$self->{attributes}} ) {
$self->{attributes}->{$attr}->{$type} = $types{$type};
}
$self->{flags}->{changed} = 1;
}
} # sub: _add_attr_type()
####################################################################
# if an attribute has changed, check all required attributes
# still exist and re-initialise the object (for PDL::Interpolate
# this is a nop)
#
sub _check_attr {
my $self = shift;
return unless $self->{flags}->{changed};
my @emsg;
foreach my $name ( keys %{ $self->{attributes} } ) {
if( $self->{attributes}->{$name}->{required} ) {
push @emsg, $name unless defined($self->{values}->{$name});
}
}
croak "ERROR - the following attributes must be supplied:\n [ @emsg ]\n"
unless $#emsg == -1;
$self->{flags}->{routine} = "none";
$self->{flags}->{status} = 1;
$self->_initialise;
$self->{flags}->{new} = 0;
} # sub: check_attr()
####################################################################
#
# method to be over-ridden by sub-classes
# PDL::Interpolate needs no initialisation
#
sub _initialise {}
####################################################################
# a version of set that ignores the settable flag
# - for use by the class, not by the public
#
# it still ignores unknown attributes
#
sub _set_value {
my $self = shift;
my %attrs = ( @_ );
foreach my $attr ( keys %attrs ) {
if ( exists($self->{values}->{$attr}) ) {
$self->{values}->{$attr} = $attrs{$attr};
$self->{flags}->{changed} = 1;
}
}
} # sub: _set_value()
# a version of get that ignores the gettable flag
# - for use by the class, not by the public
#
# an unknown attribute returns an undef
#
sub _get_value {
my $self = shift;
my @ret;
foreach my $name ( @_ ) {
if ( exists $self->{values}->{$name} ) {
push @ret, $self->{values}->{$name};
} else {
push @ret, undef;
}
}
return wantarray ? @ret : $ret[0];
} # sub: _get_value()
####################################################################
=head2 set
=for usage
my $nset = $obj->set( x = $newx, $y => $newy );
=for ref
Set attributes for a PDL::Interpolate object.
The return value gives the number of the supplied attributes
which were actually set.
=cut
sub set {
my $self = shift;
my %vals = ( @_ );
my $ctr = 0;
foreach my $name ( keys %vals ) {
if ( exists $self->{attributes}->{$name}->{settable} ) {
$self->{values}->{$name} = $vals{$name};
$ctr++;
}
}
$self->{flags}->{changed} = 1 if $ctr;
return $ctr;
} # sub: set()
####################################################################
=head2 get
=for usage
my $x = $obj->get( x );
my ( $x, $y ) = $obj->get( qw( x y ) );
=for ref
Get attributes from a PDL::Interpolate object.
Given a list of attribute names, return a list of
their values; in scalar mode return a scalar value.
If the supplied list contains an unknown attribute,
C<get> returns a value of C<undef> for that
attribute.
=cut
sub get {
my $self = shift;
my @ret;
foreach my $name ( @_ ) {
if ( exists $self->{attributes}->{$name}->{gettable} ) {
push @ret, $self->{values}->{$name};
} else {
push @ret, undef;
}
}
return wantarray ? @ret : $ret[0];
} # sub: get()
####################################################################
=head2 interpolate
=for usage
my $yi = $obj->interpolate( $xi );
=for ref
Returns the interpolated function at a given set of points.
A status value of -1, as returned by the C<status> method,
means that some of the C<$xi> points lay outside the
range of the data. The values for these points
were calculated using linear extrapolation.
=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 ) = $self->_get_value( qw( x y ) );
my ( $yi, $err ) = PDL::Primitive::interpolate( $xi, $x, $y );
if ( any $err ) {
$self->{flags}->{status} = -1;
} else {
$self->{flags}->{status} = 1;
}
$self->_set_value( err => $err );
$self->{flags}->{routine} = "interpolate";
return $yi;
}
####################################################################
#
# access to flags - have individual methods for these
=head2 status
=for usage
my $status = $obj->status;
=for ref
Returns the status of a PDL::Interpolate object
Returns B<1> if everything is okay, B<0> if
there has been a serious error since the last time
C<status> was called, and B<-1> if there
was a problem which was not serious.
In the latter case, C<$obj-E<gt>get("err")> may
provide more information, depending on the
particular class.
=cut
sub status { my $self = shift; return $self->{flags}->{status}; }
=head2 library
=for usage
my $name = $obj->library;
=for ref
Returns the name of the library used by a PDL::Interpolate object
For PDL::Interpolate, the library name is C<"PDL">.
=cut
sub library { my $self = shift; return $self->{flags}->{library}; }
=head2 routine
=for usage
my $name = $obj->routine;
=for ref
Returns the name of the last routine called by a PDL::Interpolate object.
For PDL::Interpolate, the only routine used is C<"interpolate">.
This will be more useful when calling derived classes,
in particular when trying to decode the values stored in the
C<err> attribute.
=cut
sub routine { my $self = shift; return $self->{flags}->{routine}; }
=head2 attributes
=for usage
$obj->attributes;
PDL::Interpolate::attributes;
=for ref
Print out the flags for the attributes of an object.
Useful in case the documentation is just too opaque!
=for example
PDL::Interpolate->attributes;
Flags Attribute
SGR x
SGR y
G err
G type
G bc
=cut
# note, can be called with the class, rather than just
# an object
#
# to allow this, I've used a horrible hack - we actually
# create an object and then print out the attributes from that
# Ugh!
#
sub attributes {
my $self = shift;
# ugh
$self = $self->new unless ref($self);
print "Flags Attribute\n";
while ( my ( $attr, $hashref ) = each %{$self->{attributes}} ) {
my $flag = "";
$flag .= "S" if $hashref->{settable};
$flag .= "G" if $hashref->{gettable};
$flag .= "R" if $hashref->{required};
printf " %-3s %s\n", $flag, $attr;
}
return;
}
####################################################################
=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<PDL>, perltoot(1).
=cut
####################################################################
# End with a true
1;