File Coverage

blib/lib/Math/Taylor.pm
Criterion Covered Total %
statement 120 140 85.7
branch 46 70 65.7
condition 11 30 36.6
subroutine 14 14 100.0
pod 7 7 100.0
total 198 261 75.8


line stmt bran cond sub pod time code
1             package Math::Taylor;
2              
3 2     2   36982 use 5.006;
  2         56  
  2         90  
4 2     2   12 use strict;
  2         5  
  2         70  
5 2     2   20 use warnings;
  2         4  
  2         114  
6              
7             our $VERSION = '1.00';
8              
9 2     2   1709 use Math::Symbolic qw/parse_from_string/;
  2         363119  
  2         243  
10 2     2   1914 use Math::Symbolic::MiscCalculus;
  2         4561  
  2         100  
11 2     2   12 use Carp qw/confess cluck/;
  2         4  
  2         3356  
12              
13             our $Default_Point = 0;
14             our $Default_Variable = Math::Symbolic::Variable->new('x');
15             our $Default_Remainder_Type = 'lagrange';
16              
17             =head1 NAME
18              
19             Math::Taylor - Taylor Polynomials and remainders
20              
21             =head1 SYNOPSIS
22              
23             use Math::Taylor;
24            
25             # Create new approximation
26             my $approximation = Math::Taylor->new(
27             function => "sin(y) * cos(x)",
28             point => 2,
29             variable => 'y',
30             remainder_type => 'cauchy',
31             );
32            
33             # Calculate Taylor Polynomial of degree 2
34             my $poly = $approximation->taylor_poly(2);
35             print "$poly\n";
36            
37             # Upper bounds of the remainder are also availlable:
38             my $remainder = $approximation->remainder(2, 'cauchy');
39              
40             =head1 DESCRIPTION
41              
42             Math::Taylor offers facilites to calculate Taylor Polynomials of any degree symbolically.
43             For its inner workings, it makes use of Math::Symbolic and specifically
44             Math::Symbolic::MiscCalculus.
45              
46             Math::Taylor can also calculate two types of remainders for the Taylor Series.
47              
48             =head2 EXPORT
49              
50             This module does not export any functions. You will have to use the
51             object-oriented interface.
52              
53             =head2 Methods
54              
55             =over 2
56              
57             =cut
58              
59             =item Constructor new(OPTION => ARGUMENT)
60              
61             new() is the constructor for Math::Taylor objects. It takes key => value
62             style named arguments. Valid options are 'function', 'variable', 'point'
63             and 'remainder_type'.
64              
65             new() may be called as a class method (Cnew(...)> to create
66             an object from scratch or on an existing object to clone the object. In that
67             case, the function and variable objects are I. (If you don't
68             know what that means, rest assured that it's the sane behaviour.) If you
69             use key => value pairs to set attributes, these overwrite the attributes
70             copied from the prototype.
71              
72             Any Math::Taylor object requires that at least a function attribute is defined.
73             that means, if you create objects from scratch, you have to specify
74             a C $ms_tree> attribute.
75              
76             Details on the attributes of the Math::Taylor objects can be learned from the
77             documentation of the accessor methods for these attributes (below).
78              
79             new() returns a Math::Taylor object.
80              
81             =cut
82              
83             sub new {
84 8     8 1 6976 my $proto = shift;
85 8   66     66 my $class = ref($proto) || $proto;
86              
87 8 50       25 confess "Math::Taylor called with uneven number of arguments."
88             if @_ % 2;
89              
90 8         22 my %args = @_;
91              
92             # function to approximate,
93             # variable of the function
94             # point to approximate about
95 8         49 my $self = {
96             function => undef,
97             variable => $Default_Variable,
98             point => $Default_Point,
99             remainder_type => $Default_Remainder_Type,
100             };
101              
102             # Clone prototype if applicable.
103 8 100       28 if ( ref($proto) ) {
104 2         6 $self->{function} = $proto->{function}->new();
105 2 50       131 $self->{variable} = $proto->{variable}->new()
106             if defined $proto->{variable};
107 2 50       30 $self->{point} = $proto->{point} if defined $proto->{point};
108 2 50       6 $self->{remainder_type} = $proto->{remainder_type}
109             if defined $proto->{remainder_type};
110             }
111              
112 8         13 bless $self => $class;
113              
114 8 100       35 $self->function( $args{function} ) if exists $args{function};
115 8 50       21 $self->variable( $args{variable} ) if exists $args{variable};
116 8 100       26 $self->point( $args{point} ) if exists $args{point};
117              
118 8 100       17 confess "Cannot create a Math::Taylor object without at least a function."
119             if not defined $self->function();
120              
121 7         25 return $self;
122             }
123              
124             =item Accessor function()
125              
126             This accessor can be used to get or set the function to approximate through the
127             Math::Taylor object.
128              
129             Called with no arguments, the method just returns the Math::Symbolic tree that
130             internally represents the function.
131              
132             Called with an argument, the first argument is treated as a new function for
133             the approximation and the corresponding attribute is set. The function may be
134             specified in one of two formats: Either as a Math::Symbolic tree or as a string
135             which will be parsed as a Math::Symbolic tree. For details on the syntax of
136             such strings, please refer to L and L.
137             it should, however be relatively straighforward. A few examples:
138              
139             $taylor->function('sin(x)^2/x'); # (square of the sine of x) divided by x
140             my $func = $taylor->function(); # returns the tree for the above
141             print $func."\n"; # print out the function
142             $taylor->function($anotherfunc); # set the function differently
143              
144             Please note that when setting the function to an existing Math::Symbolic tree,
145             the tree is I cloned. If you modify the tree thereafter, the modifications
146             will propagate to the function in the Math::Taylor object. This is not a bug,
147             it is a documented feature and wanted action-at-a-distance.
148              
149             When using function() to access the function attribute, the Math::Symbolic tree
150             is not cloned either.
151              
152             =cut
153              
154             sub function {
155 25     25 1 1523 my $self = shift;
156 25 100       54 if ( not @_ ) {
157 18         322 return $self->{function};
158             }
159 7         11 my $function = shift;
160              
161 7 50       34 if ( not defined $function ) {
    100          
    50          
162 0         0 confess "Won't set the function of a Math::Taylor object to 'undef'.\n";
163             }
164             elsif ( ref($function) =~ /^Math::Symbolic/ ) {
165 1         3 $self->{function} = $function;
166             }
167             elsif ( not ref($function) ) {
168 6         8 my $parsed;
169 6         9 eval { $parsed = parse_from_string($function); };
  6         25  
170 6 50 33     18414 if ( $@
      33        
171             or not defined $parsed
172             or not ref($parsed) =~ /^Math::Symbolic/ )
173             {
174 0         0 confess <<"HERE"
175             Could not parse function of Math::Taylor object as Math::Symbolic tree.
176             Argument was: '$function'
177             Error (if any) was: '$@'
178             HERE
179             }
180 6         15 $self->{function} = $parsed;
181             }
182 7         21 return $self->{function};
183             }
184              
185             =item Accessor point()
186              
187             This accessor can be used to get or set the point about which to
188             approximate using the Taylor Series. If this attribute is not set,
189             it defaults to C<0>.
190              
191             Called with no arguments, the method just returns the number.
192              
193             Called with an argument, the first argument is treated as a point to
194             approximate about and the corresponding attribute is set accordingly.
195              
196             The method always returns the current point (which should be a real
197             number).
198              
199             =cut
200              
201             sub point {
202 18     18 1 35 my $self = shift;
203 18 100       46 if ( not @_ ) {
204 13         38 return $self->{point};
205             }
206 5         14 my $point = shift;
207 5 50       10 if ( defined $point ) {
208 5         9 $self->{point} = $point;
209             }
210             else {
211 0         0 confess
212             "Cannot set the 'point' attribute of a Math::Taylor object to 'undef'.";
213             }
214 5         11 return $self->{point};
215             }
216              
217             =item Accessor variable()
218              
219             This accessor can be used to get or set the variable in respect to which
220             the function should be approximated. If the variable attribute remains
221             unset, it defaults to 'x'.
222              
223             Called with no arguments, the method just returns the Math::Symbolic::Variable
224             object which internally represents the variable. You can use this object
225             in a string to interpolate as the variable name.
226              
227             Called with an argument, the first argument is treated as a new variable
228             in respect to which the function should be approximated. The variable
229             may be specified either as a string which will be parsed as the name of
230             a new Math::Symbolic::Variable object or as an existing
231             Math::Symbolic::Variable.
232              
233             The method always returns the current variable.
234              
235             When retrieving or setting the variable as a Math::Symbolic::Variable object,
236             the object is not cloned.
237              
238             Please refer to L for details.
239              
240             =cut
241              
242             sub variable {
243 12     12 1 497 my $self = shift;
244 12 100       33 if ( not @_ ) {
245 10         30 return $self->{variable};
246             }
247 2         5 my $variable = shift;
248 2 50 33     18 if (
      33        
249             defined $variable
250             and ( ref($variable) eq 'Math::Symbolic::Variable'
251             or not ref($variable) )
252             )
253             {
254 2 50       6 if ( not ref($variable) ) {
255 2         2 my $parsed;
256 2         3 eval { $parsed = Math::Symbolic::Variable->new($variable); };
  2         8  
257 2 50 33     47 if ( $@
      33        
258             or not defined $parsed
259             or not ref($parsed) =~ /^Math::Symbolic::Variable/ )
260             {
261 0         0 confess <<"HERE"
262             Could not parse variable of Math::Taylor object as Math::Symbolic::Variable.
263             Argument was: '$variable'
264             Error (if any) was: '$@'
265             HERE
266             }
267 2         4 $variable = $parsed;
268             }
269              
270             # Is the variable contained in the function at all?
271 2         8 $self->_is_variable_in_function( $self->{function}, $variable );
272             }
273             else {
274 0 0       0 confess
275             "Tried to create a variable for Math::Taylor object from dubious"
276             . "source. Source: '"
277             . ( !defined($variable) ? 'undef' : $variable ) . "'";
278             }
279 1         10 $self->{variable} = $variable;
280 1         6 return $variable;
281             }
282              
283             # Internal method to test whether a given variable is actually part of
284             # a function's signature.
285             sub _is_variable_in_function {
286 2     2   2 my $self = shift;
287 2         4 my $function = shift;
288 2         3 my $var = shift;
289 2         9 my %signature = map { ( $_, undef ) } $function->explicit_signature();
  1         125  
290 2 100       9 if ( not exists $signature{ $var->to_string() } ) {
291 1         16 confess <<"HERE";
292             Variable not contained in function to approximate.
293             Variable: '$var'
294             Function: '$function'
295             HERE
296             }
297             }
298              
299             =item Accessor remainder_type()
300              
301             This accessor can be used to get or set the type of remainder
302             of the Taylor Series. If this attribute is not set,
303             it defaults to C.
304              
305             Called with no arguments, the method just returns the remainder type.
306              
307             Called with an argument, the first argument is treated as a name of a
308             remainder type to calculate. Valid values are either 'lagrange' or 'cauchy'.
309              
310             For details, I have to refer you to the documentation of
311             L.
312              
313             The method always returns the current remainder type.
314              
315             =cut
316              
317             sub remainder_type {
318 1     1 1 1536 my $self = shift;
319 1 50       5 if ( not @_ ) {
320 0         0 return $self->{remainder_type};
321             }
322 1         3 my $err = shift;
323 1 50 33     10 if ( defined $err and $err eq 'lagrange' or $err eq 'cauchy' ) {
      33        
324 1         2 $self->{remainder_type} = $err;
325             }
326             else {
327 0         0 confess
328             "Cannot set the 'remainder_type' attribute of a Math::Taylor object to anything\n"
329             . "but 'cauchy' or 'lagrange'.";
330             }
331 1         3 return $self->{remainder_type};
332             }
333              
334              
335              
336             =item taylor_poly()
337              
338             This method calculates the Taylor polynomial of specified degree or of
339             degree 1 if none has been specified. The polynomial is returned as a Math::Symbolic tree.
340              
341             Optional argument is the degree of the polynomial. Zeroth degree is the first
342             element of the series. That means, it's just the function evaluated at the
343             point of approximation.
344              
345             =cut
346              
347             sub taylor_poly {
348 3     3 1 2851 my $self = shift;
349 3         7 my $degree = shift;
350 3 50       10 $degree = 1 if not defined $degree;
351 3 50       8 confess("The degree of a Taylor approximation has to be >= 0.")
352             unless $degree >= 0;
353              
354             # Get all necessary data.
355 3         10 my $function = $self->function()->new();
356 3         203 my $variable = $self->variable();
357 3         8 my $pos = $self->point();
358              
359             # Make sure we don't have any vars in the function that clash with
360             # the nomenclature of the output of TaylorPolynomial:
361             # If TaylorPolynomial uses "x" as variable, it will include "x_0" as
362             # a new variable in the output.
363 3         12 my $posname = $variable->to_string() . '_0';
364 3         33 my %sig = map {($_, undef)} $function->explicit_signature();
  3         130  
365 3         5 my @replace;
366 3 50       10 if (exists $sig{$posname}) {
367 0         0 my $newname = $posname;
368 0         0 while (exists $sig{$newname}) {
369 0         0 $newname .= '_';
370             }
371 0         0 $function->implement($posname => $newname);
372 0         0 @replace = ($newname, $posname);
373             }
374              
375 3         15 my $poly = Math::Symbolic::MiscCalculus::TaylorPolynomial(
376             $function,
377             $degree,
378             $variable,
379             $pos
380             );
381            
382 3 50 33     2918 if (not defined $poly or not ref($poly) =~ /^Math::Symbolic/) {
383 0         0 confess(
384             "Could not calculate Taylor approximation of degree $degree using\n"
385             ."function '$function',\n position '$pos',\n and variable '$variable'."
386             );
387             }
388              
389             # Insert value for x_0
390 3         10 $poly->implement($posname => $pos);
391            
392             # Undo all changes to variable names.
393 3 50       6061 if (@replace) {
394 0         0 $poly->implement(@replace);
395             }
396              
397 3         29 return $poly;
398             }
399              
400              
401              
402             =item remainder()
403              
404             This method calculates and returns the remainder of a Taylor polynomial of
405             specified degree. If no degree (>= 0) is specified as first argument,
406             degree 1 is assumed.
407              
408             Depending on what has been set as remainder_type, the calculated
409             remainder may be either the Lagrange Remainder or the Cauchy Remainder.
410              
411             The method takes two arguments, both optional. The first is the degree as
412             stated above. The second is the name of a new variable introduced to the
413             remainder term. This variable is called I in the documentation
414             of Math::Symbolic::MiscCalculus and ranges between 0 and 1. The default name is
415             thus I. Be careful when you are approximation a formula containing a
416             variable of that name.
417              
418             For details, refer to the following web pages and Perl modules:
419              
420             L
421              
422             I
423             A Wolfram Web Resource. http://mathworld.wolfram.com/LagrangeRemainder.html>
424              
425             I
426             A Wolfram Web Resource. http://mathworld.wolfram.com/CauchyRemainder.html>
427              
428             =cut
429              
430             sub remainder {
431 6     6 1 3174 my $self = shift;
432 6         8 my $degree = shift;
433 6 100       16 $degree = 1 if not defined $degree;
434 6 50       12 confess("The degree of a Taylor approximation has to be >= 0.")
435             unless $degree >= 0;
436            
437 6         10 my $tvar = shift;
438 6 100       18 $tvar = Math::Symbolic::Variable->new('theta') if not defined $tvar;
439 6         42 $tvar = Math::Symbolic::Variable->new($tvar);
440            
441             # Get all necessary data.
442 6         95 my $function = $self->function()->new();
443 6         593 my $variable = $self->variable();
444 6         13 my $pos = $self->point();
445 6         10 my $type = $self->{remainder_type};
446              
447             # Make sure we don't have any vars in the function that clash with
448             # the nomenclature of the output of TaylorPolynomial:
449             # If TaylorPolynomial uses "x" as variable, it will include "x_0" as
450             # a new variable in the output.
451 6         17 my $posname = $variable->to_string() . '_0';
452 6         52 my %sig = map {($_, undef)} $function->explicit_signature();
  6         404  
453 6         12 my @replace;
454 6 50       12 if (exists $sig{$posname}) {
455 0         0 my $newname = $posname;
456 0         0 while (exists $sig{$newname}) {
457 0         0 $newname .= '_';
458             }
459 0         0 $function->implement($posname => $newname);
460 0         0 @replace = ($newname, $posname);
461             }
462              
463             # get remainder;
464 6         8 my $rem;
465 6 100       11 if ($type eq 'lagrange') {
466 3         11 $rem = Math::Symbolic::MiscCalculus::TaylorErrorLagrange(
467             $function, $degree, $variable, $pos, $tvar
468             );
469             } else {
470 3         12 $rem = Math::Symbolic::MiscCalculus::TaylorErrorCauchy(
471             $function, $degree, $variable, $pos, $tvar
472             );
473             }
474            
475             # Insert value for x_0
476 6         21531 $rem->implement($posname => $pos);
477            
478             # Undo all changes to variable names.
479 6 50       9918 if (@replace) {
480 0         0 $rem->implement(@replace);
481             }
482              
483 6         46 return $rem;
484             }
485              
486              
487              
488              
489              
490             1;
491             __END__