File Coverage

blib/lib/Math/Calculus/TaylorSeries.pm
Criterion Covered Total %
statement 1 3 33.3
branch n/a
condition n/a
subroutine 1 1 100.0
pod n/a
total 2 4 50.0


line stmt bran cond sub pod time code
1             # ########################################################################################
2             # A TAYLOR SERIES OBJECT
3             # This module takes an expression stored in a Math::Calculus::Expression object and
4             # returns the Taylor Series of it.
5             # Copyright (C) Jonathan Worthington 2005
6             # This module may be used and distributed under the same terms as Perl.
7             # ########################################################################################
8            
9             package Math::Calculus::TaylorSeries;
10 1     1   13445 use Math::Calculus::Expression;
  0            
  0            
11             use strict;
12             our $VERSION = '0.1';
13             our @ISA = qw/Math::Calculus::Expression/;
14             our $MAXITERATIONS = 100;
15            
16             =head1 NAME
17            
18             Math::Calculus::TaylorSeries - Decomposition of an expression into its Taylor Series
19            
20             =head1 SYNOPSIS
21            
22             use Math::Calculus::TaylorSeries;
23            
24             # Create an object.
25             my $exp = Math::Calculus::TaylorSeries->new;
26            
27             # Set a variable and expression.
28             $exp->addVariable('x');
29             $exp->setExpression('sin(x)') or die $exp->getError;
30            
31             # Get expression object for first 4 terms about x = 0.
32             my $result = $exp->taylorSeries('x', 4, 0) or die $exp->getError;
33             print $result->getExpression; # Prints x - x^3/6 + x^5/120 - x^7/5040
34            
35            
36             =head1 DESCRIPTION
37            
38             This module can take an algebraic expression, parses it and then decomposes it into
39             a Taylor series, returning a new expression containing the first N elements.
40            
41             It understands expressions containing any of the operators +, -, *, / and ^ (raise to
42             power), bracketed expressions to enable correct precedence and the functions ln,
43             exp, sin, cos, tan, sec, cosec, cot, sinh, cosh, tanh, sech, cosech, coth, asin,
44             acos, atan, asinh, acosh and atanh.
45            
46             =head1 EXPORT
47            
48             None by default.
49            
50             =head1 METHODS
51            
52             =cut
53            
54             # Constructor
55             # ###########
56            
57             =item new
58            
59             $exp = Math::Calculus::TaylorSeries->new;
60            
61             Creates a new instance of the Taylor Series object, which can hold an individual
62             expression.
63            
64             =item addVariable
65            
66             $exp->addVariable('x');
67            
68             Sets a certain named value in the expression as being a variable. A named value must be
69             an alphabetic chracter.
70            
71             =item setExpression
72            
73             $exp->setExpression('x^2 + 5*x);
74            
75             Takes an expression in human-readable form and stores it internally as a tree structure,
76             checking it is a valid expression that the module can understand in the process. Note that
77             the engine is strict about syntax. For example, note above that you must write 5*x and not
78             just 5x. Whitespace is allowed in the expression, but does not have any effect on precedence.
79             If you require control of precedence, use brackets; bracketed expressions will always be
80             evaluated first, as you would normally expect. The module follows the BODMAS precedence
81             convention. Returns undef on failure and a true value on success.
82            
83             =item getExpression
84            
85             $expr = $exp->getExpression;
86            
87             Returns a textaul, human readable representation of the expression that is being stored.
88            
89             =cut
90            
91            
92             # Taylor Series.
93             # ##############
94            
95             =item taylorSeries
96            
97             $result = $exp->taylorSeries($variable, $terms, $about);
98            
99             Finds the first $terms non-zero terms of the Taylor series of the expression object
100             for the variable $variable evaluated about the value $about and returns a new
101             expression object that represents it.
102             =cut
103            
104             sub taylorSeries {
105             # Get invocant and variable.
106             my ($self, $variable, $terms, $about) = @_;
107            
108             # Clear error and traceback.
109             $self->{'error'} = $self->{'traceback'} = '';
110            
111             # Check variable is in the list of variables.
112             unless (grep { $_ eq $variable } @{$self->{'variables'}})
113             {
114             $self->{'error'} = 'Function variable was not declared.';
115             return undef;
116             }
117            
118             # Check number of terms is sane.
119             unless ($terms =~ /^\d+$/ && $terms > 1)
120             {
121             $self->{'error'} = 'Attempt to evaluate Taylor series with an invalid number of terms.';
122             return undef;
123             }
124            
125             # Check about value is sane.
126             unless ($about =~ /^[\-\d\.]+$/)
127             {
128             $self->{'error'} = 'Attempt to evaluate Taylor series about an invalid value.';
129             return undef;
130             }
131            
132             # Create a clone of the expression object that we'll differentiate and prepare to find co-efficients.
133             my $diffExp = $self->clone;
134             my @coeffs = ();
135             my $coeffsFound = 0;
136            
137             # Loop until we've found enough terms or we hit our maximum number of iterations.
138             my $numIters = 0;
139             while ($coeffsFound < $terms) {
140             # Evaluate.
141             my $coeff = $diffExp->evaluate($variable => $about);
142             return undef unless defined($coeff);
143            
144             # Put in co-effs list, and if it's non-zero then state we've found a term.
145             push @coeffs, $coeff;
146             $coeffsFound++ if $coeff != 0;
147            
148             # Differentiate for next round.
149             return undef unless $diffExp->differentiate($variable);
150             $diffExp->simplify;
151            
152             # Sanity check - we may run out of terms.
153             last if ++$numIters == $MAXITERATIONS;
154             }
155            
156             # Now we need to generate the expression with the real co-efficients.
157             my @termList = ();
158             for (my $i = 0; $i < @coeffs; $i++) {
159             # If the co-efficient is non-zero, create the term and put it on the list.
160             if ($coeffs[$i] != 0) {
161             my $term = $coeffs[$i];
162             $term .= '*' . $variable if $i > 0;
163             $term .= '^' . $i if $i > 1;
164             $term .= '/' . $self->fact($i);
165             push @termList, $term;
166             }
167             }
168            
169             # Create a new expression object containing the term, and simplify.
170             my $newExp = Math::Calculus::Expression->new;
171             unless ($newExp->setExpression(join '+', @termList)) {
172             $self->{'error'} = "Could not parse generated taylor series expression.";
173             }
174             $newExp->simplify;
175            
176             # Return the Taylor series, if no errors.
177             if ($self->{'error'}) {
178             return undef;
179             } else {
180             return $newExp;
181             }
182             }
183            
184            
185             # Taylor Series Co-efficients.
186             # ############################
187            
188             =item taylorSeries_coeffs
189            
190             $result = $exp->taylorSeries($variable, $numcoeffs, $about);
191            
192             Returns an array containing the first $numcoeffs terms when the Taylor series for
193             the variable $variable is found about $about.
194             =cut
195            
196             sub taylorSeries_coeffs {
197             # Get invocant and variable.
198             my ($self, $variable, $numCoeffs, $about) = @_;
199            
200             # Clear error and traceback.
201             $self->{'error'} = $self->{'traceback'} = '';
202            
203             # Check variable is in the list of variables.
204             unless (grep { $_ eq $variable } @{$self->{'variables'}})
205             {
206             $self->{'error'} = 'Function variable was not declared.';
207             return ();
208             }
209            
210             # Check number of co-efficients is sane.
211             unless ($numCoeffs =~ /^\d+$/)
212             {
213             $self->{'error'} = 'Attempt to evaluate Taylor series with an invalid number of terms.';
214             return ();
215             }
216            
217             # Check about value is sane.
218             unless ($about =~ /^[\-\d\.]+$/)
219             {
220             $self->{'error'} = 'Attempt to evaluate Taylor series about an invalid value.';
221             return ();
222             }
223            
224             # Create a clone of the expression object that we'll differentiate and prepare to find co-efficients.
225             my $diffExp = $self->clone;
226             my @coeffs = ();
227            
228             # Loop until we've found enough co-efficients.
229             my $numIters = 0;
230             while ($numIters < $numCoeffs) {
231             # Evaluate.
232             my $coeff = $diffExp->evaluate($variable => $about);
233             return () unless defined($coeff);
234            
235             # Put in co-effs list.
236             push @coeffs, $coeff;
237            
238             # Differentiate for next round.
239             return undef unless $diffExp->differentiate($variable);
240             $diffExp->simplify;
241            
242             # Increment counter.
243             $numIters++;
244             }
245            
246             # Return the list, if no errors.
247             if ($self->{'error'}) {
248             return ();
249             } else {
250             return @coeffs;
251             }
252             }
253            
254            
255             =item getTraceback
256            
257             $exp->getTraceback;
258            
259             When setExpression and taylorSeries are called, a traceback is generated to describe
260             what these functions did. If an error occurs, this traceback can be extremely useful
261             in helping track down the source of the error.
262            
263             =item getError
264            
265             $exp->getError;
266            
267             When any method other than getTraceback is called, the error message stored is cleared, and
268             then any errors that occur during the execution of the method are stored. If failure occurs,
269             call this method to get a textual representation of the error.
270            
271             =head1 SEE ALSO
272            
273             The author of this module has a website at L, which has
274             the latest news about the module and a web-based frontend to allow you to test the module
275             out for yourself.
276            
277             =head1 AUTHOR
278            
279             Jonathan Worthington, Ejonathan@jwcs.netE
280            
281             =head1 COPYRIGHT AND LICENSE
282            
283             Copyright (C) 2004 by Jonathan Worthington
284            
285             This library is free software; you can redistribute it and/or modify
286             it under the same terms as Perl itself, either Perl version 5.8.1 or,
287             at your option, any later version of Perl 5 you may have available.
288            
289             =cut
290            
291            
292             # Factorial routine.
293             sub fact {
294             return $_[1] == 0 ? 1 : $_[1] * $_[0]->fact($_[1] - 1);
295             }
296            
297            
298             1;