File Coverage

blib/lib/Math/Symbolic/Compiler.pm
Criterion Covered Total %
statement 64 70 91.4
branch 13 20 65.0
condition 3 5 60.0
subroutine 10 10 100.0
pod 3 3 100.0
total 93 108 86.1


line stmt bran cond sub pod time code
1             =encoding utf8
2              
3             =head1 NAME
4              
5             Math::Symbolic::Compiler - Compile Math::Symbolic trees to Perl code
6              
7             =head1 SYNOPSIS
8              
9             use Math::Symbolic::Compiler;
10            
11             # A tree to compile
12             my $tree = Math::Symbolic->parse_from_string('a^2 + b * c * 2');
13            
14             # The Math::Symbolic::Variable 'a' will be evaluated to $_[1], etc.
15             my $vars = [qw(b a c)];
16            
17             my ($closure, $code, $trees) =
18             Math::Symbolic::Compiler->compile($tree, $vars);
19            
20             print $closure->(2, 3, 5); # (b, a, c)
21             # prints 29 (= 3^2 + 2 * 5 * 2)
22            
23             # or:
24             ($closure, $trees) =
25             Math::Symbolic::Compiler->compile_to_sub($tree, $vars);
26            
27             ($code, $trees) = Math::Symbolic::Compiler->compile_to_code($tree, $vars);
28              
29             =head1 DESCRIPTION
30              
31             This module allows one to compile Math::Symbolic trees to Perl code and/or
32             anonymous subroutines whose arguments will be positionally mapped to the
33             variables of the compiled Math::Symbolic tree.
34              
35             The reason you'd want to do this is that evaluating a Math::Symbolic tree to
36             its numeric value is extremely slow. So is compiling, but once you've done all
37             necessary symbolic calculations, you can take advantage of the speed gain
38             of invoking a closure instead of evaluating a tree.
39              
40             =head2 UNCOMPILED LEFTOVER TREES
41              
42             Not all, however, is well in the land of compiled Math::Symbolic trees.
43             There may occasionally be trees that cannot be compiled (such as a derivative)
44             which need to be included into the code as trees. These trees will be
45             returned in a referenced array by the compile*() methods. The closures
46             will have access to
47             the required trees as a special variable '@_TREES inside the closure's scope,
48             so you need not worry about them in that case. But if you plan to use the
49             generated code itself, you need to supply an array named @_TREES that
50             contains the trees as returned by the compile*() methods in the scope of
51             the eval() you evaluate the code with.
52              
53             Note that you give away all performance benefits compiling the tree might have
54             if the closure contains uncompiled trees. You can tell there are any by
55             checking the length of the referenced array that contains the trees. If it's
56             0, then there are no trees left to worry about.
57              
58             =head2 AVOIDING LEFTOVER TREES
59              
60             In most cases, this is pretty simple. Just apply all derivatives in the tree
61             to make sure that there are none left in the tree. As of version 0.130, there
62             is no operator except derivatives that cannot be compiled. There may, however,
63             be some operators you cannot get rid of this easily some time in the future.
64             If you have problems getting a tree to compile, try using the means of
65             simplification provided by Math::Symbolic::* to get a simpler tree for
66             compilation.
67              
68             =head2 EXPORT
69              
70             None by default, but you may choose to import the compile(), compile_to_sub(),
71             and compile_to_code() subroutines to your namespace using the standard
72             Exporter semantics including the ':all' tag.
73              
74             =head1 SUBROUTINES
75              
76             =cut
77              
78             package Math::Symbolic::Compiler;
79              
80 23     23   419 use 5.006;
  23         84  
81 23     23   143 use strict;
  23         45  
  23         560  
82 23     23   100 use warnings;
  23         38  
  23         1382  
83              
84 23     23   136 use Math::Symbolic::ExportConstants qw/:all/;
  23         53  
  23         9786  
85              
86             our @ISA = qw(Exporter);
87              
88             our %EXPORT_TAGS = (
89             'all' => [
90             qw(
91             compile
92             compile_to_sub
93             compile_to_code
94             )
95             ]
96             );
97              
98             our @EXPORT_OK = ( @{ $EXPORT_TAGS{'all'} } );
99              
100             our @EXPORT = qw();
101              
102             our $VERSION = '0.613';
103              
104             =head2 ($code, $trees) = compile_to_code($tree, $vars)
105              
106             The compile_to_code() class method takes one mandatory argument which is
107             the Math::Symbolic tree to be compiled. Second argument is optional
108             and an array reference to an array of variable mappings.
109             See L for details on how this works.
110              
111             compile_to_code() returns a string and an array reference. The string
112             contains the compiled Perl code that uses the values stored in @_ as described
113             in the section on positional variable passing. It also accesses a special
114             variable @_TREES if there were any sub-trees (inside the tree that has been
115             compiled) that were impossible to compile. The array reference returned by this
116             method contains any of the aforementioned trees that failed to compile.
117              
118             If there are any such trees that did not compile, you may put them into the
119             @_TREES variable in scope of the eval() that evaluates the compiled code
120             in the same order that they were returned by this method. If you do that, the
121             code will run and determine the value of the tree at run-time. Needless to say,
122             that is slow.
123              
124             =cut
125              
126             sub compile_to_code {
127 21     21 1 2240 my $tree = shift;
128 21 50 33     115 $tree = shift if not ref $tree and $tree eq __PACKAGE__;
129              
130 21   100     59 my $order = shift || [];
131 21         50 my %order;
132 21 50       109 if (ref($order) eq 'HASH') {
    50          
133 0         0 %order = %$order;
134             }
135             elsif (ref($order) eq 'ARRAY') {
136 21         38 my $count = 0;
137 21         55 %order = map { ( $_, $count++ ) } @$order;
  9         23  
138             }
139              
140 23     23   169 no warnings 'recursion';
  23         42  
  23         20744  
141              
142 21         88 my $vars = [ $tree->explicit_signature() ];
143              
144 21         55 my %vars;
145             my @not_placed;
146 21         48 foreach (@$vars) {
147 51         80 my $pos = $order{$_};
148 51 100       95 if ( defined $pos ) {
149 9         15 $vars{$_} = $pos;
150             }
151             else {
152 42         83 push @not_placed, $_;
153             }
154             }
155              
156 21         32 my $count = 0;
157 21         54 foreach ( sort @not_placed ) {
158 42         121 $vars{$_} = @$vars - @not_placed + $count++;
159             }
160              
161             # The user is to do that himself. Left in to show that it would be
162             # a sensible (if slow) thing to do.
163             # $tree = $tree->simplify();
164             # $tree = $tree->apply_derivatives();
165             # $tree = $tree->simplify();
166              
167 21         37 my @trees;
168              
169 21         82 my $code = _rec_ms_to_sub( $tree, \%vars, \@trees );
170              
171 21         217 return ( $code, \@trees );
172             }
173              
174             =head2 ($sub, $trees) = compile_to_sub($tree, $vars)
175              
176             The compile_to_sub() class method takes one mandatory argument which is
177             the Math::Symbolic tree to be compiled. Second argument is optional
178             and an array reference to an array of variable mappings.
179             See L for details on how this works.
180              
181             compile_to_sub() returns a list of two elements, the first being the compiled
182             anonymous subroutine. For details on the second element, please refer to
183             the docs on the compile_to_code() subroutine.
184              
185             =cut
186              
187             sub compile_to_sub {
188 10     10 1 129 my ( $code, $trees ) = Math::Symbolic::Compiler::compile_to_code(@_);
189 10         50 my $sub = _compile_sub( 'sub {' . $code . '}', @$trees );
190 10         87 return ( $sub, $trees );
191             }
192              
193             =head2 ($sub, $code, $trees) = compile($tree, $vars)
194              
195             The compile() class method takes one mandatory argument which is
196             the Math::Symbolic tree to be compiled. Second argument is optional
197             and an array reference to an array of variable mappings.
198             See L for details on how this works.
199              
200             compile() returns a list of three elements, the first being the compiled
201             anonymous subroutine, the second being the compiled code. For details on the
202             second and third elements, please refer to the docs on the compile_to_code()
203             subroutine.
204              
205             =cut
206              
207             sub compile {
208 1     1 1 1046 my ( $code, $trees ) = Math::Symbolic::Compiler::compile_to_code(@_);
209 1         4 my $sub = _compile_sub( 'sub {' . $code . '}', @$trees );
210 1         18 return ( $sub, $code, $trees );
211             }
212              
213             sub _compile_sub {
214 11     11   16 my @_TREES;
215 11 50       30 @_TREES = @_[ 1 .. $#_ ] if @_ > 1;
216 11         1761 my $sub = eval $_[0];
217 11 50       51 die "$@" if $@;
218 11         49 return $sub;
219             }
220              
221             sub _rec_ms_to_sub {
222 349     349   583 my $tree = shift;
223 349         565 my $vars = shift;
224 349         527 my $trees = shift;
225              
226 349         552 my $code = '';
227 349         924 my $ttype = $tree->term_type();
228              
229 349 100       777 if ( $ttype == T_CONSTANT ) {
    100          
230 83         253 $code .= $tree->value();
231             }
232             elsif ( $ttype == T_VARIABLE ) {
233 95         235 $code .= '$_[' . $vars->{ $tree->name() } . ']';
234             }
235             else {
236 171         404 my $type = $tree->type();
237 171         360 my $otype = $Math::Symbolic::Operator::Op_Types[$type];
238 171         317 my $app = $otype->{application};
239 171 50       332 if ( ref($app) eq 'CODE' ) {
240 0         0 push @$trees, $tree->new();
241             my $arg_str = join( ', ',
242 0         0 map { "'$_' => \$_[" . $vars->{$_} . ']' } keys %$vars );
  0         0  
243 0         0 my $index = $#$trees;
244 0         0 $code .= <
245             (\$_TREES[$index]->value($arg_str))
246             HERE
247             }
248             else {
249 171         1063 my @app = split /\$_\[(\d+)\]/, $app;
250 171 50       430 if ( @app > 1 ) {
251 171         408 for ( my $i = 1 ; $i < @app ; $i += 2 ) {
252             $app[$i] = '('
253 328         992 . _rec_ms_to_sub( $tree->{operands}[ $app[$i] ],
254             $vars, $trees )
255             . ')';
256             }
257             }
258 171         619 $code .= join '', @app;
259             }
260             }
261 349         1217 return $code;
262             }
263              
264             1;
265             __END__