File Coverage

blib/lib/Math/Symbolic/Derivative.pm
Criterion Covered Total %
statement 221 241 91.7
branch 131 174 75.2
condition 13 15 86.6
subroutine 17 17 100.0
pod 2 2 100.0
total 384 449 85.5


line stmt bran cond sub pod time code
1              
2             =encoding utf8
3              
4             =head1 NAME
5              
6             Math::Symbolic::Derivative - Derive Math::Symbolic trees
7              
8             =head1 SYNOPSIS
9              
10             use Math::Symbolic::Derivative qw/:all/;
11             $derived = partial_derivative($term, $variable);
12             # or:
13             $derived = total_derivative($term, $variable);
14              
15             =head1 DESCRIPTION
16              
17             This module implements derivatives for Math::Symbolic trees.
18             Derivatives are Math::Symbolic::Operators, but their implementation
19             is drawn from this module because it is significantly more complex
20             than the implementation of most operators.
21              
22             Derivatives come in two flavours. There are partial- and total derivatives.
23              
24             Explaining the precise difference between partial- and total derivatives is
25             beyond the scope of this document, but in the context of Math::Symbolic,
26             the difference is simply that partial derivatives just derive in terms of
27             I dependency on the differential variable while total derivatives
28             recongnize implicit dependencies from variable signatures.
29              
30             Partial derivatives are faster, have been tested more thoroughly, and
31             are probably what you want for simpler applications anyway.
32              
33             =head2 EXPORT
34              
35             None by default. But you may choose to import the total_derivative()
36             and partial_derivative() functions.
37              
38             =cut
39              
40             package Math::Symbolic::Derivative;
41              
42 23     23   389 use 5.006;
  23         77  
  23         883  
43 23     23   124 use strict;
  23         43  
  23         658  
44 23     23   109 use warnings;
  23         50  
  23         656  
45 23     23   123 no warnings 'recursion';
  23         42  
  23         935  
46              
47 23     23   150 use Carp;
  23         65  
  23         1528  
48              
49 23     23   227 use Math::Symbolic::ExportConstants qw/:all/;
  23         64  
  23         123372  
50              
51             require Exporter;
52              
53             our @ISA = qw(Exporter);
54              
55             our %EXPORT_TAGS = (
56             'all' => [
57             qw(
58             &total_derivative
59             &partial_derivative
60             )
61             ]
62             );
63              
64             our @EXPORT_OK = ( @{ $EXPORT_TAGS{'all'} } );
65              
66             our @EXPORT = qw();
67              
68             our $VERSION = '0.612';
69              
70             =head1 CLASS DATA
71              
72             The package variable %Partial_Rules contains partial
73             derivative rules as key-value pairs of names and subroutines.
74              
75             =cut
76              
77             # lookup-table for derivative rules for various operators.
78             our %Rules = (
79             'each operand' => \&_each_operand,
80             'product rule' => \&_product_rule,
81             'quotient rule' => \&_quotient_rule,
82             'logarithmic chain rule after ln' => \&_logarithmic_chain_rule_after_ln,
83             'logarithmic chain rule' => \&_logarithmic_chain_rule,
84             'derivative commutation' => \&_derivative_commutation,
85             'trigonometric derivatives' => \&_trigonometric_derivatives,
86             'inverse trigonometric derivatives' => \&_inverse_trigonometric_derivatives,
87             'inverse atan2' => \&_inverse_atan2,
88             );
89              
90             # References to derivative subroutines
91             # Will be assigned a reference after subroutine compilation.
92             our $Partial_Sub;
93             our $Total_Sub;
94              
95             our @Constant_Simplify = (
96             # B_SUM
97             sub {
98             my $tree = shift;
99             my ($op1, $op2) = @{$tree->{operands}};
100             my ($t1, $t2) = ($op1->term_type(), $op2->term_type());
101             if ($t1 == T_CONSTANT) {
102             return $op2 if $op1->{value} == 0;
103             if ($t2 == T_CONSTANT) {
104             return Math::Symbolic::Constant->new($op1->{value} + $op2->{value});
105             }
106             }
107             elsif ($t2 == T_CONSTANT) {
108             return $op1 if $op2->{value} == 0;
109             }
110              
111             return $tree;
112             },
113              
114             # B_DIFFERENCE
115             sub {
116             my $tree = shift;
117             my ($op1, $op2) = @{$tree->{operands}};
118             my ($t1, $t2) = ($op1->term_type(), $op2->term_type());
119             if ($t1 == T_CONSTANT) {
120             $op2 *= -1, return $op2 if $op1->{value} == 0;
121             if ($t2 == T_CONSTANT) {
122             return Math::Symbolic::Constant->new($op1->{value} - $op2->{value});
123             }
124             }
125             elsif ($t2 == T_CONSTANT) {
126             return $op1 if $op2->{value} == 0;
127             $op2->{value} *= -1;
128             return Math::Symbolic::Operator->new('+', $op1, $op2);
129             }
130             return $tree;
131             },
132            
133             # B_PRODUCT
134             undef, # implemented inline
135             # B_DIVISION
136             undef, # not implemented
137              
138             # U_MINUS
139             sub {
140             my $tree = shift;
141             my $op = $tree->{operands}[0];
142             if ($op->term_type == T_CONSTANT) {
143             return Math::Symbolic::Constant->new(-$op->{value});
144             }
145             return $tree;
146             },
147              
148             #... not implemented
149             );
150              
151             =begin comment
152              
153             The following subroutines are helper subroutines that apply a
154             specific rule to a tree.
155              
156             =end comment
157              
158             =cut
159              
160             sub _each_operand {
161 63     63   297 my ( $tree, $var, $cloned, $d_sub ) = @_;
162 63         1517 foreach ( @{ $tree->{operands} } ) {
  63         196  
163 102         333 $_ = $d_sub->( $_, $var, 1 );
164             }
165              
166 63         235 my $type = $tree->type();
167 63         123 my $simplifier = $Constant_Simplify[$type];
168 63 50       253 return $simplifier->($tree) if $simplifier;
169              
170 0         0 return $tree;
171             }
172              
173              
174             sub _product_rule {
175 157     157   280 my ( $tree, $var, $cloned, $d_sub ) = @_;
176 157         280 my $ops = $tree->{operands};
177 157         266 my ($o1, $o2) = @$ops;
178 157         469 my ($to1, $to2) = ($o1->term_type(), $o2->term_type());
179              
180             # one of the terms is a constant, don't derive it
181 157 100       448 if ($to1 == T_CONSTANT) {
182 88 50       271 return Math::Symbolic::Constant->zero() if $o1->{value} == 0;
183 88         349 my $deriv = $d_sub->( $o2, $var, 0 );
184 88 50       263 return $deriv if $o1->{value} == 0;
185 88 100       250 return Math::Symbolic::Constant->new($deriv->{value}*$o1->{value})
186             if $deriv->term_type == T_CONSTANT;
187             }
188 99 100       365 if ($to2 == T_CONSTANT) {
189 1 50       5 return Math::Symbolic::Constant->zero() if $o2->{value} == 0;
190 1         5 my $deriv = $d_sub->( $o1, $var, 0 );
191 1 50       4 return $deriv if $o2->{value} == 0;
192 1 50       4 return Math::Symbolic::Constant->new($deriv->{value}*$o2->{value})
193             if $deriv->term_type == T_CONSTANT;
194             }
195            
196 98         290 my $do1 = $d_sub->( $o1, $var, 0 );
197 98         234 my $do2 = $d_sub->( $o2, $var, 0 );
198              
199 98         344 my ($tdo1, $tdo2) = ($do1->term_type(), $do2->term_type());
200              
201 98         149 my ($m1, $m2);
202             # check for const*const
203 98 100       246 if ($tdo1 == T_CONSTANT) {
204 69 50       353 if ($to2 == T_CONSTANT) {
    100          
    100          
205 0         0 $m1 = $do1->new($o2->{value} * $do1->{value}); # const
206             } elsif ($do1->{value} == 0) {
207 37         441 $m1 = $do1->zero(); # 0
208             } elsif ($do1->{value} == 1) {
209 10         18 $m1 = $o2;
210             } else {
211 22         119 $m1 = $do1*$o2; # c*tree
212             }
213             }
214             else {
215 29         138 $m1 = $o2*$do1;
216             }
217              
218 98 100       245 if ($tdo2 == T_CONSTANT) {
219 16 50       86 if ($to1 == T_CONSTANT) {
    100          
    50          
220 0         0 $m2 = $do2->new($o1->{value} * $do2->{value}); # const
221             } elsif ($do2->{value} == 0) {
222 8         28 $m2 = $do2->zero(); # 0
223             } elsif ($do2->{value} == 1) {
224 8         15 $m2 = $o1;
225             } else {
226 0         0 $m2 = $do2*$o1; # c*tree
227             }
228             }
229             else {
230 82         315 $m2 = $o1*$do2;
231             }
232              
233             # 0's or 2 consts in +
234 98 100       328 if ($m1->term_type == T_CONSTANT) {
    100          
235 37 50       277 return $m2 if $m1->{value} == 0;
236 0 0       0 if ($m2->term_type == T_CONSTANT) {
237 0         0 return $m2->new($m1->{value}*$m2->{value});
238             }
239             }
240             elsif ($m2->term_type == T_CONSTANT) {
241 6 50       49 return $m1 if $m2->{value} == 0;
242             }
243              
244 55         208 return Math::Symbolic::Operator->new( '+', $m1, $m2 );
245             }
246              
247             sub _quotient_rule {
248 32     32   79 my ( $tree, $var, $cloned, $d_sub ) = @_;
249              
250 32         98 my ($op1, $op2) = @{$tree->{operands}};
  32         93  
251              
252 32         56 my ($do1, $do2);
253              
254             # y = f(x)/c; y' = f'/c
255 32 100       344 if ($op2->is_simple_constant()) {
    100          
256 3         13 $do1 = $d_sub->( $op1, $var, 0 );
257 3         14 my $val = $op2->value();
258              
259 3 50       25 if ($val == 0) {
    50          
260 0         0 return $tree->new('/', $do1, $op2->new()); # inf!
261             }
262             elsif ($val == 1) {
263 0         0 return $do1; # f/1
264             }
265 3         20 return $tree->new('*', Math::Symbolic::Constant->new(1/$val), $do1);
266             }
267             # y = c/f(x) => y' = -c*f'(x)/f^2(x)
268             elsif ($op1->is_simple_constant()) {
269 13         43 $do2 = $d_sub->( $op2, $var, 0 );
270 13         58 my $val = $op1->value();
271            
272 13 50       50 if ($val == 0) {
273 0         0 return Math::Symbolic::Constant->zero(); # 0*f'/f
274             }
275              
276 13         49 my $tdo2 = $do2->term_type();
277 13 100       41 if ($tdo2 == T_CONSTANT) {
278 5 100       35 return $do2->zero() if $do2->{value} == 0; # c*0/f
279 4         23 return $tree->new(
280             '/', $do2->new(-1.*$val*$do2->{value}),
281             $tree->new('^', $op2, 2)
282             );
283             }
284             else {
285 8         47 return $tree->new(
286             '*', Math::Symbolic::Constant->new(-1*$val),
287             $tree->new('/', $do2, $tree->new('^', $op2, Math::Symbolic::Constant->new(2)))
288             )
289             }
290             }
291              
292 16 50       89 $do1 = $d_sub->( $op1, $var, 0 ) if not $do1;
293 16 50       58 $do2 = $d_sub->( $op2, $var, 0 ) if not $do2;
294              
295 16         68 my $m1 = Math::Symbolic::Operator->new( '*', $do1, $op2 );
296 16         61 my $m2 = Math::Symbolic::Operator->new( '*', $op1, $do2 );
297              
298             # f' = 0
299 16 100       130 if ($do1->is_zero()) {
    100          
300 1         3 $m1 = undef;
301             }
302             # f' = 1
303             elsif ($do1->is_one()) {
304 3         11 $m1 = $op2->new();
305             }
306              
307             # g' = 0
308 16 50       119 if ($do2->is_zero()) {
    100          
309 0         0 $m2 = undef;
310             }
311             elsif ($do2->is_one()) {
312 1         4 $m2 = $op1->new();
313             }
314              
315 16         31 my $upper;
316             # -g'f / g^2
317 16 100       67 if (not defined $m1) {
    50          
318             # f'=g'=0
319 1 50       6 return Math::Symbolic::Constant->zero() if not defined $m2;
320 1         4 $upper = $tree->new('neg', $m2);
321             }
322             # f'g / g^2 = f'/g
323             elsif (not defined $m2) {
324 0         0 return $tree->new('/', $do1, $op2);
325             }
326              
327 16         79 my $m3 = $tree->new('^', $op2, Math::Symbolic::Constant->new(2));
328 16 100       44 if (not defined $upper) {
329 15         60 $upper = Math::Symbolic::Operator->new( '-', $m1, $m2 );
330             }
331 16         138 return Math::Symbolic::Operator->new( '/', $upper, $m3 );
332             }
333              
334             sub _logarithmic_chain_rule_after_ln {
335 67     67   146 my ( $tree, $var, $cloned, $d_sub ) = @_;
336              
337             # y(x)=u^v
338             # y'(x)=y*(d/dx ln(y))
339             # y'(x)=y*(d/dx (v*ln(u)))
340 67         106 my ($u, $v) = @{$tree->{operands}};
  67         183  
341              
342             # This is a special case:
343             # y(x)=u^CONST
344             # y'(x)=CONST*y* d/dx ln(u)
345             # y'(x)=CONST*y* u' / u
346 67 100       229 if ($v->term_type() == T_CONSTANT) {
347              
348             # y=VAR^CONST
349 40 100       131 if ($u->term_type() == T_VARIABLE) {
350 13         31 my $d = $d_sub->($u, $var, 0);
351 13         41 my $dtt = $d->term_type();
352 13 50       54 if ($dtt == T_CONSTANT) {
353             # not our var
354 13 50       34 return Math::Symbolic::Constant->zero() if $d->{value} == 0;
355             # our var
356 13 50       49 return Math::Symbolic::Constant->one() if $v->{value} == 1;
357 13 100       46 return $tree->new('*', $v->new(), $u->new()) if $v->{value} == 2;
358 8         28 return $tree->new('*', $v->new(), $tree->new('^', $u->new(), $v->new($v->{value}-1)));
359             }
360             # otherwise: signature contains $var
361             }
362 27         98 return Math::Symbolic::Operator->new(
363             '*',
364             Math::Symbolic::Operator->new(
365             '*', $v->new(), $tree
366             ),
367             Math::Symbolic::Operator->new(
368             '/', $d_sub->($u, $var, 0), $u->new()
369             )
370             );
371             }
372              
373 27         143 my $e = Math::Symbolic::Constant->euler();
374 27         107 my $ln = Math::Symbolic::Operator->new( 'log', $e, $u );
375 27         109 my $mul1 = $ln->new( '*', $v, $ln );
376 27         94 my $dmul = $d_sub->( $mul1, $var, 0 );
377 27         103 $tree = $ln->new( '*', $tree, $dmul );
378 27         157 return $tree;
379             }
380              
381             sub _logarithmic_chain_rule {
382 30     30   66 my ( $tree, $var, $cloned, $d_sub ) = @_;
383              
384             #log_a(y(x))=>y'(x)/(ln(a)*y(x))
385 30         45 my ($a, $y) = @{$tree->{operands}};
  30         81  
386 30         86 my $dy = $d_sub->( $y, $var, 0 );
387              
388             # This would be y'/y
389 30 100 100     104 if ($a->term_type() == T_CONSTANT and $a->{special} eq 'euler') {
390 26         98 return Math::Symbolic::Operator->new('/', $dy, $y);
391             }
392            
393 4         19 my $e = Math::Symbolic::Constant->euler();
394 4         18 my $ln = Math::Symbolic::Operator->new( 'log', $e, $a );
395 4         16 my $mul1 = $ln->new( '*', $ln, $y->new() );
396 4         15 $tree = $ln->new( '/', $dy, $mul1 );
397 4         12 return $tree;
398             }
399              
400             sub _derivative_commutation {
401 7     7   20 my ( $tree, $var, $cloned, $d_sub ) = @_;
402 7         44 $tree->{operands}[0] = $d_sub->( $tree->{operands}[0], $var, 0 );
403 7         42 return $tree;
404             }
405              
406             sub _trigonometric_derivatives {
407 99     99   159 my ( $tree, $var, $cloned, $d_sub ) = @_;
408 99         314 my $op = Math::Symbolic::Operator->new();
409 99         391 my $d_inner = $d_sub->( $tree->{operands}[0], $var, 0 );
410 99         120 my $trig;
411 99         295 my $type = $tree->type();
412 99 100 66     387 if ( $type == U_SINE ) {
    100          
    100          
    100          
    50          
413 29         102 $trig = $op->new( 'cos', $tree->{operands}[0] );
414             }
415             elsif ( $type == U_COSINE ) {
416 52         190 $trig = $op->new( 'neg', $op->new( 'sin', $tree->{operands}[0] ) );
417             }
418             elsif ( $type == U_SINE_H ) {
419 5         22 $trig = $op->new( 'cosh', $tree->{operands}[0] );
420             }
421             elsif ( $type == U_COSINE_H ) {
422 5         22 $trig = $op->new( 'sinh', $tree->{operands}[0] );
423             }
424             elsif ( $type == U_TANGENT or $type == U_COTANGENT ) {
425 8         37 $trig = $op->new(
426             '/',
427             Math::Symbolic::Constant->one(),
428             $op->new(
429             '^',
430             $op->new( 'cos', $tree->op1() ),
431             Math::Symbolic::Constant->new(2)
432             )
433             );
434 8 100       50 $trig = $op->new( 'neg', $trig ) if $type == U_COTANGENT;
435             }
436             else {
437 0         0 die "Trigonometric derivative applied to invalid operator.";
438             }
439 99 50       315 if ($d_inner->term_type() == T_CONSTANT) {
440 99         283 my $spec = $d_inner->special();
441 99 100       324 if ($spec eq 'zero') {
    100          
442 1         5 return $d_inner;
443             }
444             elsif ($spec eq 'one') {
445 73         369 return $trig;
446             }
447             }
448 25         88 return $op->new( '*', $d_inner, $trig );
449             }
450              
451             sub _inverse_trigonometric_derivatives {
452 6     6   13 my ( $tree, $var, $cloned, $d_sub ) = @_;
453 6         31 my $op = Math::Symbolic::Operator->new();
454 6         29 my $d_inner = $d_sub->( $tree->{operands}[0], $var, 0 );
455 6         11 my $trig;
456 6         24 my $type = $tree->type();
457 6 100 100     118 if ( $type == U_ARCSINE or $type == U_ARCCOSINE ) {
    100 100        
    50 66        
458 2 100       11 my $one = $type == U_ARCSINE
459             ? Math::Symbolic::Constant->one()
460             : Math::Symbolic::Constant->new(-1);
461 2         10 $trig = $op->new( '/', $one,
462             $op->new( '-', $one->new(1), $op->new( '^', $tree->op1(), $one->new(2) ) )
463             );
464             }
465             elsif ($type == U_ARCTANGENT
466             or $type == U_ARCCOTANGENT )
467             {
468 2 100       13 my $one = $type == U_ARCTANGENT
469             ? Math::Symbolic::Constant->one()
470             : Math::Symbolic::Constant->new(-1);
471 2         9 $trig = $op->new( '/', $one,
472             $op->new( '+', $one->new(1), $op->new( '^', $tree->op1(), $one->new(2) ) )
473             );
474             }
475             elsif ($type == U_AREASINE_H
476             or $type == U_AREACOSINE_H )
477             {
478 2         9 my $one = Math::Symbolic::Constant->one();
479 2 100       11 $trig = $op->new(
480             '/', $one,
481             $op->new(
482             '^',
483             $op->new(
484             ( $tree->type() == U_AREASINE_H ? '+' : '-' ),
485             $op->new( '^', $tree->op1(), $one->new(2) ),
486             $one
487             ),
488             $one->new(0.5)
489             )
490             );
491             }
492             else {
493 0         0 die "Inverse trig. derivative applied to invalid operator.";
494             }
495              
496 6 50       28 if ($d_inner->term_type() == T_CONSTANT) {
497 6         40 my $spec = $d_inner->special();
498 6 50       34 if ($spec eq 'zero') {
    50          
499 0         0 return $d_inner;
500             }
501             elsif ($spec eq 'one') {
502 0         0 return $trig;
503             }
504             }
505 6         29 return $op->new( '*', $d_inner, $trig );
506             }
507              
508             sub _inverse_atan2 {
509 1     1   3 my ( $tree, $var, $cloned, $d_sub ) = @_;
510             # d/df atan(y/x) = x^2/(x^2+y^2) * (d/df y/x)
511 1         2 my ($op1, $op2) = @{$tree->{operands}};
  1         4  
512              
513 1         6 my $inner = $d_sub->( $op1->new()/$op2->new(), $var, 0 );
514             # templates
515 1         10 my $two = Math::Symbolic::Constant->new(2);
516 1         6 my $op = Math::Symbolic::Operator->new('+', $two, $two);
517              
518 1         8 my $result = $op->new('*',
519             $op->new('/',
520             $op->new('^', $op2->new(), $two->new()),
521             $op->new(
522             '+', $op->new('^', $op2->new(), $two->new()),
523             $op->new('^', $op1->new(), $two->new())
524             )
525             ),
526             $inner
527             );
528 1         7 return $result;
529             }
530              
531             =head1 SUBROUTINES
532              
533             =cut
534              
535             =head2 partial_derivative
536              
537             Takes a Math::Symbolic tree and a Math::Symbolic::Variable as argument.
538             third argument is an optional boolean indicating whether or not the
539             tree has to be cloned before being derived. If it is true, the
540             subroutine happily stomps on any code that might rely on any components
541             of the Math::Symbolic tree that was passed to the sub as first argument.
542              
543             =cut
544              
545             sub partial_derivative {
546 449     449 1 978 my $tree = shift;
547 449         518 my $var = shift;
548 449 50       921 defined $var or die "Cannot derive using undefined variable.";
549 449 100       1507 if ( ref($var) eq '' ) {
550 10         46 $var = Math::Symbolic::parse_from_string($var);
551 10 50       310 croak "2nd argument to partial_derivative must be variable."
552             if ( ref($var) ne 'Math::Symbolic::Variable' );
553             }
554             else {
555 439 50       1019 croak "2nd argument to partial_derivative must be variable."
556             if ( ref($var) ne 'Math::Symbolic::Variable' );
557             }
558              
559 449         613 my $cloned = shift;
560              
561 449 100       884 if ( not $cloned ) {
562 383         1092 $tree = $tree->new();
563 383         599 $cloned = 1;
564             }
565              
566 449 100       1241 if ( $tree->term_type() == T_OPERATOR ) {
    100          
    50          
567 276         787 my $rulename =
568             $Math::Symbolic::Operator::Op_Types[ $tree->type() ]->{derive};
569 276         603 my $subref = $Rules{$rulename};
570              
571 276 50       623 die "Cannot derive using rule '$rulename'."
572             unless defined $subref;
573 276         944 $tree = $subref->( $tree, $var, $cloned, $Partial_Sub );
574             }
575             elsif ( $tree->term_type() == T_CONSTANT ) {
576 59         193 $tree = Math::Symbolic::Constant->zero();
577             }
578             elsif ( $tree->term_type() == T_VARIABLE ) {
579 114 100       358 if ( $tree->name() eq $var->name() ) {
580 96         390 $tree = Math::Symbolic::Constant->one;
581             }
582             else {
583 18         75 $tree = Math::Symbolic::Constant->zero;
584             }
585             }
586             else {
587 0         0 die "Cannot apply partial derivative to anything but a tree.";
588             }
589              
590 449         2082 return $tree;
591             }
592              
593             =head2 total_derivative
594              
595             Takes a Math::Symbolic tree and a Math::Symbolic::Variable as argument.
596             third argument is an optional boolean indicating whether or not the
597             tree has to be cloned before being derived. If it is true, the
598             subroutine happily stomps on any code that might rely on any components
599             of the Math::Symbolic tree that was passed to the sub as first argument.
600              
601             =cut
602              
603             sub total_derivative {
604 283     283 1 351 my $tree = shift;
605 283         322 my $var = shift;
606 283 50       536 defined $var or die "Cannot derive using undefined variable.";
607 283 50       604 if ( ref($var) eq '' ) {
608 0         0 $var = Math::Symbolic::parse_from_string($var);
609 0 0       0 croak "Second argument to total_derivative must be variable."
610             if ( ref($var) ne 'Math::Symbolic::Variable' );
611             }
612             else {
613 283 50       639 croak "Second argument to total_derivative must be variable."
614             if ( ref($var) ne 'Math::Symbolic::Variable' );
615             }
616              
617 283         345 my $cloned = shift;
618              
619 283 100       510 if ( not $cloned ) {
620 247         654 $tree = $tree->new();
621 247         358 $cloned = 1;
622             }
623              
624 283 100       794 if ( $tree->term_type() == T_OPERATOR ) {
    100          
    50          
625 191         673 my $var_name = $var->name();
626 191         586 my @tree_sig = $tree->signature();
627 191 100       344 if ( ( grep { $_ eq $var_name } @tree_sig ) > 0 ) {
  197         603  
628 186         744 my $rulename =
629             $Math::Symbolic::Operator::Op_Types[ $tree->type() ]->{derive};
630 186         383 my $subref = $Rules{$rulename};
631              
632 186 50       391 die "Cannot derive using rule '$rulename'."
633             unless defined $subref;
634 186         577 $tree = $subref->( $tree, $var, $cloned, $Total_Sub );
635             }
636             else {
637 5         19 $tree = Math::Symbolic::Constant->zero();
638             }
639             }
640             elsif ( $tree->term_type() == T_CONSTANT ) {
641 10         42 $tree = Math::Symbolic::Constant->zero();
642             }
643             elsif ( $tree->term_type() == T_VARIABLE ) {
644 82         201 my $name = $tree->name();
645 82         252 my $var_name = $var->name();
646              
647 82 100       183 if ( $name eq $var_name ) {
648 79         310 $tree = Math::Symbolic::Constant->one;
649             }
650             else {
651 3         10 my @tree_sig = $tree->signature();
652 3         9 my $is_dependent;
653 3         6 foreach my $ident (@tree_sig) {
654 5 100       15 if ( $ident eq $var_name ) {
655 3         4 $is_dependent = 1;
656 3         6 last;
657             }
658             }
659 3 50       22 if ( $is_dependent ) {
660 3         12 $tree =
661             Math::Symbolic::Operator->new( 'total_derivative', $tree,
662             $var );
663             }
664             else {
665 0         0 $tree = Math::Symbolic::Constant->zero;
666             }
667             }
668             }
669             else {
670 0         0 die "Cannot apply total derivative to anything but a tree.";
671             }
672              
673 283         1134 return $tree;
674             }
675              
676             # Class data again.
677             $Partial_Sub = \&partial_derivative;
678             $Total_Sub = \&total_derivative;
679              
680             1;
681             __END__