File Coverage

blib/lib/Math/Symbolic/Derivative.pm
Criterion Covered Total %
statement 220 240 91.6
branch 131 174 75.2
condition 13 15 86.6
subroutine 17 17 100.0
pod 2 2 100.0
total 383 448 85.4


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   371 use 5.006;
  23         81  
43 23     23   131 use strict;
  23         38  
  23         501  
44 23     23   86 use warnings;
  23         34  
  23         1052  
45 23     23   102 no warnings 'recursion';
  23         54  
  23         990  
46              
47 23     23   110 use Carp;
  23         36  
  23         1677  
48              
49 23     23   138 use Math::Symbolic::ExportConstants qw/:all/;
  23         38  
  23         109136  
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.613';
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   191 my ( $tree, $var, $cloned, $d_sub ) = @_;
162 63         107 foreach ( @{ $tree->{operands} } ) {
  63         197  
163 102         356 $_ = $d_sub->( $_, $var, 1 );
164             }
165              
166 63         196 my $type = $tree->type();
167 63         149 my $simplifier = $Constant_Simplify[$type];
168 63 50       270 return $simplifier->($tree) if $simplifier;
169              
170 0         0 return $tree;
171             }
172              
173              
174             sub _product_rule {
175 157     157   374 my ( $tree, $var, $cloned, $d_sub ) = @_;
176 157         382 my $ops = $tree->{operands};
177 157         374 my ($o1, $o2) = @$ops;
178 157         430 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       403 if ($to1 == T_CONSTANT) {
182 88 50       224 return Math::Symbolic::Constant->zero() if $o1->{value} == 0;
183 88         295 my $deriv = $d_sub->( $o2, $var, 0 );
184 88 50       289 return $deriv if $o1->{value} == 0;
185             return Math::Symbolic::Constant->new($deriv->{value}*$o1->{value})
186 88 100       220 if $deriv->term_type == T_CONSTANT;
187             }
188 99 100       309 if ($to2 == T_CONSTANT) {
189 1 50       6 return Math::Symbolic::Constant->zero() if $o2->{value} == 0;
190 1         15 my $deriv = $d_sub->( $o1, $var, 0 );
191 1 50       6 return $deriv if $o2->{value} == 0;
192             return Math::Symbolic::Constant->new($deriv->{value}*$o2->{value})
193 1 50       4 if $deriv->term_type == T_CONSTANT;
194             }
195            
196 98         334 my $do1 = $d_sub->( $o1, $var, 0 );
197 98         279 my $do2 = $d_sub->( $o2, $var, 0 );
198              
199 98         313 my ($tdo1, $tdo2) = ($do1->term_type(), $do2->term_type());
200              
201 98         206 my ($m1, $m2);
202             # check for const*const
203 98 100       252 if ($tdo1 == T_CONSTANT) {
204 69 50       370 if ($to2 == T_CONSTANT) {
    100          
    100          
205 0         0 $m1 = $do1->new($o2->{value} * $do1->{value}); # const
206             } elsif ($do1->{value} == 0) {
207 37         120 $m1 = $do1->zero(); # 0
208             } elsif ($do1->{value} == 1) {
209 10         20 $m1 = $o2;
210             } else {
211 22         109 $m1 = $do1*$o2; # c*tree
212             }
213             }
214             else {
215 29         153 $m1 = $o2*$do1;
216             }
217              
218 98 100       248 if ($tdo2 == T_CONSTANT) {
219 16 50       69 if ($to1 == T_CONSTANT) {
    100          
    50          
220 0         0 $m2 = $do2->new($o1->{value} * $do2->{value}); # const
221             } elsif ($do2->{value} == 0) {
222 8         33 $m2 = $do2->zero(); # 0
223             } elsif ($do2->{value} == 1) {
224 8         21 $m2 = $o1;
225             } else {
226 0         0 $m2 = $do2*$o1; # c*tree
227             }
228             }
229             else {
230 82         294 $m2 = $o1*$do2;
231             }
232              
233             # 0's or 2 consts in +
234 98 100       292 if ($m1->term_type == T_CONSTANT) {
    100          
235 37 50       392 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       47 return $m1 if $m2->{value} == 0;
242             }
243              
244 55         165 return Math::Symbolic::Operator->new( '+', $m1, $m2 );
245             }
246              
247             sub _quotient_rule {
248 32     32   126 my ( $tree, $var, $cloned, $d_sub ) = @_;
249              
250 32         63 my ($op1, $op2) = @{$tree->{operands}};
  32         102  
251              
252 32         67 my ($do1, $do2);
253              
254             # y = f(x)/c; y' = f'/c
255 32 100       300 if ($op2->is_simple_constant()) {
    100          
256 3         11 $do1 = $d_sub->( $op1, $var, 0 );
257 3         9 my $val = $op2->value();
258              
259 3 50       16 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         17 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         40 $do2 = $d_sub->( $op2, $var, 0 );
270 13         48 my $val = $op1->value();
271            
272 13 50       44 if ($val == 0) {
273 0         0 return Math::Symbolic::Constant->zero(); # 0*f'/f
274             }
275              
276 13         40 my $tdo2 = $do2->term_type();
277 13 100       41 if ($tdo2 == T_CONSTANT) {
278 5 100       22 return $do2->zero() if $do2->{value} == 0; # c*0/f
279             return $tree->new(
280 4         21 '/', $do2->new(-1.*$val*$do2->{value}),
281             $tree->new('^', $op2, 2)
282             );
283             }
284             else {
285 8         41 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       79 $do1 = $d_sub->( $op1, $var, 0 ) if not $do1;
293 16 50       63 $do2 = $d_sub->( $op2, $var, 0 ) if not $do2;
294              
295 16         72 my $m1 = Math::Symbolic::Operator->new( '*', $do1, $op2 );
296 16         49 my $m2 = Math::Symbolic::Operator->new( '*', $op1, $do2 );
297              
298             # f' = 0
299 16 100       137 if ($do1->is_zero()) {
    100          
300 1         5 $m1 = undef;
301             }
302             # f' = 1
303             elsif ($do1->is_one()) {
304 3         8 $m1 = $op2->new();
305             }
306              
307             # g' = 0
308 16 50       112 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         40 my $upper;
316             # -g'f / g^2
317 16 100       73 if (not defined $m1) {
    50          
318             # f'=g'=0
319 1 50       5 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         83 my $m3 = $tree->new('^', $op2, Math::Symbolic::Constant->new(2));
328 16 100       70 if (not defined $upper) {
329 15         79 $upper = Math::Symbolic::Operator->new( '-', $m1, $m2 );
330             }
331 16         48 return Math::Symbolic::Operator->new( '/', $upper, $m3 );
332             }
333              
334             sub _logarithmic_chain_rule_after_ln {
335 67     67   192 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         126 my ($u, $v) = @{$tree->{operands}};
  67         186  
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       186 if ($v->term_type() == T_CONSTANT) {
347              
348             # y=VAR^CONST
349 40 100       107 if ($u->term_type() == T_VARIABLE) {
350 13         33 my $d = $d_sub->($u, $var, 0);
351 13         35 my $dtt = $d->term_type();
352 13 50       33 if ($dtt == T_CONSTANT) {
353             # not our var
354 13 50       55 return Math::Symbolic::Constant->zero() if $d->{value} == 0;
355             # our var
356 13 50       65 return Math::Symbolic::Constant->one() if $v->{value} == 1;
357 13 100       48 return $tree->new('*', $v->new(), $u->new()) if $v->{value} == 2;
358 8         23 return $tree->new('*', $v->new(), $tree->new('^', $u->new(), $v->new($v->{value}-1)));
359             }
360             # otherwise: signature contains $var
361             }
362 27         89 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         96 my $e = Math::Symbolic::Constant->euler();
374 27         86 my $ln = Math::Symbolic::Operator->new( 'log', $e, $u );
375 27         73 my $mul1 = $ln->new( '*', $v, $ln );
376 27         83 my $dmul = $d_sub->( $mul1, $var, 0 );
377 27         72 $tree = $ln->new( '*', $tree, $dmul );
378 27         141 return $tree;
379             }
380              
381             sub _logarithmic_chain_rule {
382 30     30   78 my ( $tree, $var, $cloned, $d_sub ) = @_;
383              
384             #log_a(y(x))=>y'(x)/(ln(a)*y(x))
385 30         86 my ($a, $y) = @{$tree->{operands}};
  30         81  
386 30         108 my $dy = $d_sub->( $y, $var, 0 );
387              
388             # This would be y'/y
389 30 100 100     84 if ($a->term_type() == T_CONSTANT and $a->{special} eq 'euler') {
390 26         105 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         14 my $mul1 = $ln->new( '*', $ln, $y->new() );
396 4         28 $tree = $ln->new( '/', $dy, $mul1 );
397 4         2109 return $tree;
398             }
399              
400             sub _derivative_commutation {
401 7     7   24 my ( $tree, $var, $cloned, $d_sub ) = @_;
402 7         43 $tree->{operands}[0] = $d_sub->( $tree->{operands}[0], $var, 0 );
403 7         23 return $tree;
404             }
405              
406             sub _trigonometric_derivatives {
407 99     99   288 my ( $tree, $var, $cloned, $d_sub ) = @_;
408 99         268 my $op = Math::Symbolic::Operator->new();
409 99         386 my $d_inner = $d_sub->( $tree->{operands}[0], $var, 0 );
410 99         167 my $trig;
411 99         286 my $type = $tree->type();
412 99 100 66     398 if ( $type == U_SINE ) {
    100          
    100          
    100          
    50          
413 29         296 $trig = $op->new( 'cos', $tree->{operands}[0] );
414             }
415             elsif ( $type == U_COSINE ) {
416 52         180 $trig = $op->new( 'neg', $op->new( 'sin', $tree->{operands}[0] ) );
417             }
418             elsif ( $type == U_SINE_H ) {
419 5         15 $trig = $op->new( 'cosh', $tree->{operands}[0] );
420             }
421             elsif ( $type == U_COSINE_H ) {
422 5         20 $trig = $op->new( 'sinh', $tree->{operands}[0] );
423             }
424             elsif ( $type == U_TANGENT or $type == U_COTANGENT ) {
425 8         30 $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       30 $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       320 if ($d_inner->term_type() == T_CONSTANT) {
440 99         319 my $spec = $d_inner->special();
441 99 100       403 if ($spec eq 'zero') {
    100          
442 1         5 return $d_inner;
443             }
444             elsif ($spec eq 'one') {
445 73         501 return $trig;
446             }
447             }
448 25         88 return $op->new( '*', $d_inner, $trig );
449             }
450              
451             sub _inverse_trigonometric_derivatives {
452 6     6   17 my ( $tree, $var, $cloned, $d_sub ) = @_;
453 6         13 my $op = Math::Symbolic::Operator->new();
454 6         17 my $d_inner = $d_sub->( $tree->{operands}[0], $var, 0 );
455 6         10 my $trig;
456 6         13 my $type = $tree->type();
457 6 100 100     65 if ( $type == U_ARCSINE or $type == U_ARCCOSINE ) {
    100 100        
    50 66        
458 2 100       7 my $one = $type == U_ARCSINE
459             ? Math::Symbolic::Constant->one()
460             : Math::Symbolic::Constant->new(-1);
461 2         5 $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       8 my $one = $type == U_ARCTANGENT
469             ? Math::Symbolic::Constant->one()
470             : Math::Symbolic::Constant->new(-1);
471 2         6 $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         7 my $one = Math::Symbolic::Constant->one();
479 2 100       8 $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       50 if ($d_inner->term_type() == T_CONSTANT) {
497 6         13 my $spec = $d_inner->special();
498 6 50       33 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         15 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         3  
512              
513 1         3 my $inner = $d_sub->( $op1->new()/$op2->new(), $var, 0 );
514             # templates
515 1         5 my $two = Math::Symbolic::Constant->new(2);
516 1         3 my $op = Math::Symbolic::Operator->new('+', $two, $two);
517              
518 1         3 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         5 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 1218 my $tree = shift;
547 449         639 my $var = shift;
548 449 50       910 defined $var or die "Cannot derive using undefined variable.";
549 449 100       981 if ( ref($var) eq '' ) {
550 10         53 $var = Math::Symbolic::parse_from_string($var);
551 10 50       256 croak "2nd argument to partial_derivative must be variable."
552             if ( ref($var) ne 'Math::Symbolic::Variable' );
553             }
554             else {
555 439 50       987 croak "2nd argument to partial_derivative must be variable."
556             if ( ref($var) ne 'Math::Symbolic::Variable' );
557             }
558              
559 449         712 my $cloned = shift;
560              
561 449 100       855 if ( not $cloned ) {
562 383         1092 $tree = $tree->new();
563 383         602 $cloned = 1;
564             }
565              
566 449 100       1155 if ( $tree->term_type() == T_OPERATOR ) {
    100          
    50          
567             my $rulename =
568 276         611 $Math::Symbolic::Operator::Op_Types[ $tree->type() ]->{derive};
569 276         634 my $subref = $Rules{$rulename};
570              
571 276 50       920 die "Cannot derive using rule '$rulename'."
572             unless defined $subref;
573 276         883 $tree = $subref->( $tree, $var, $cloned, $Partial_Sub );
574             }
575             elsif ( $tree->term_type() == T_CONSTANT ) {
576 59         211 $tree = Math::Symbolic::Constant->zero();
577             }
578             elsif ( $tree->term_type() == T_VARIABLE ) {
579 114 100       334 if ( $tree->name() eq $var->name() ) {
580 96         335 $tree = Math::Symbolic::Constant->one;
581             }
582             else {
583 18         70 $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         1549 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 487 my $tree = shift;
605 283         512 my $var = shift;
606 283 50       677 defined $var or die "Cannot derive using undefined variable.";
607 283 50       691 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       660 croak "Second argument to total_derivative must be variable."
614             if ( ref($var) ne 'Math::Symbolic::Variable' );
615             }
616              
617 283         465 my $cloned = shift;
618              
619 283 100       594 if ( not $cloned ) {
620 247         669 $tree = $tree->new();
621 247         506 $cloned = 1;
622             }
623              
624 283 100       781 if ( $tree->term_type() == T_OPERATOR ) {
    100          
    50          
625 191         601 my $var_name = $var->name();
626 191         536 my @tree_sig = $tree->signature();
627 191 100       430 if ( ( grep { $_ eq $var_name } @tree_sig ) > 0 ) {
  197         589  
628             my $rulename =
629 186         463 $Math::Symbolic::Operator::Op_Types[ $tree->type() ]->{derive};
630 186         435 my $subref = $Rules{$rulename};
631              
632 186 50       400 die "Cannot derive using rule '$rulename'."
633             unless defined $subref;
634 186         9240 $tree = $subref->( $tree, $var, $cloned, $Total_Sub );
635             }
636             else {
637 5         22 $tree = Math::Symbolic::Constant->zero();
638             }
639             }
640             elsif ( $tree->term_type() == T_CONSTANT ) {
641 10         36 $tree = Math::Symbolic::Constant->zero();
642             }
643             elsif ( $tree->term_type() == T_VARIABLE ) {
644 82         200 my $name = $tree->name();
645 82         233 my $var_name = $var->name();
646              
647 82 100       177 if ( $name eq $var_name ) {
648 79         289 $tree = Math::Symbolic::Constant->one;
649             }
650             else {
651 3         16 my @tree_sig = $tree->signature();
652 3         9 my $is_dependent;
653 3         37 foreach my $ident (@tree_sig) {
654 5 100       16 if ( $ident eq $var_name ) {
655 3         8 $is_dependent = 1;
656 3         9 last;
657             }
658             }
659 3 50       13 if ( $is_dependent ) {
660 3         15 $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         909 return $tree;
674             }
675              
676             # Class data again.
677             $Partial_Sub = \&partial_derivative;
678             $Total_Sub = \&total_derivative;
679              
680             1;
681             __END__