line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
package Math::Symbolic::Custom::Transformation; |
2
|
|
|
|
|
|
|
|
3
|
1
|
|
|
1
|
|
193409
|
use 5.006; |
|
1
|
|
|
|
|
7
|
|
|
1
|
|
|
|
|
48
|
|
4
|
1
|
|
|
1
|
|
6
|
use strict; |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
38
|
|
5
|
1
|
|
|
1
|
|
6
|
use warnings; |
|
1
|
|
|
|
|
7
|
|
|
1
|
|
|
|
|
40
|
|
6
|
|
|
|
|
|
|
|
7
|
1
|
|
|
1
|
|
6
|
use Carp qw/croak carp/; |
|
1
|
|
|
|
|
1
|
|
|
1
|
|
|
|
|
87
|
|
8
|
1
|
|
|
1
|
|
6
|
use Math::Symbolic qw/:all/; |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
260
|
|
9
|
1
|
|
|
1
|
|
1067
|
use Math::Symbolic::Custom::Pattern; |
|
1
|
|
|
|
|
9857
|
|
|
1
|
|
|
|
|
887
|
|
10
|
|
|
|
|
|
|
require Math::Symbolic::Custom::Transformation::Group; |
11
|
|
|
|
|
|
|
|
12
|
|
|
|
|
|
|
require Exporter; |
13
|
|
|
|
|
|
|
|
14
|
|
|
|
|
|
|
our @ISA = qw(Exporter); |
15
|
|
|
|
|
|
|
|
16
|
|
|
|
|
|
|
our $VERSION = '2.02'; |
17
|
|
|
|
|
|
|
|
18
|
|
|
|
|
|
|
=encoding utf8 |
19
|
|
|
|
|
|
|
|
20
|
|
|
|
|
|
|
=head1 NAME |
21
|
|
|
|
|
|
|
|
22
|
|
|
|
|
|
|
Math::Symbolic::Custom::Transformation - Transform Math::Symbolic trees |
23
|
|
|
|
|
|
|
|
24
|
|
|
|
|
|
|
=head1 SYNOPSIS |
25
|
|
|
|
|
|
|
|
26
|
|
|
|
|
|
|
use Math::Symbolic::Custom::Transformation; |
27
|
|
|
|
|
|
|
my $trafo = Math::Symbolic::Custom::Transformation->new( |
28
|
|
|
|
|
|
|
'TREE_x + TREE_x' => '2 * TREE_x' |
29
|
|
|
|
|
|
|
); |
30
|
|
|
|
|
|
|
|
31
|
|
|
|
|
|
|
my $modified = $trafo->apply($math_symbolic_tree); |
32
|
|
|
|
|
|
|
if (defined $modified) { |
33
|
|
|
|
|
|
|
print "Outermost operator is a sum of two identical trees.\n"; |
34
|
|
|
|
|
|
|
print "Transformed it into a product. ($modified)\n"; |
35
|
|
|
|
|
|
|
} |
36
|
|
|
|
|
|
|
else { |
37
|
|
|
|
|
|
|
print "Transformation could not be applied.\n"; |
38
|
|
|
|
|
|
|
} |
39
|
|
|
|
|
|
|
|
40
|
|
|
|
|
|
|
# shortcut: new_trafo |
41
|
|
|
|
|
|
|
use Math::Symbolic::Custom::Transformation qw/new_trafo/; |
42
|
|
|
|
|
|
|
|
43
|
|
|
|
|
|
|
# use the value() function to have the transformation compute the value |
44
|
|
|
|
|
|
|
# of the expression after the replacements. simplify{} works similar. |
45
|
|
|
|
|
|
|
my $another_trafo = new_trafo( |
46
|
|
|
|
|
|
|
'TREE_foo / CONST_bar' => 'value{1/CONST_bar} * TREE_foo' |
47
|
|
|
|
|
|
|
); |
48
|
|
|
|
|
|
|
|
49
|
|
|
|
|
|
|
# If you'll need the same transformation but don't want to keep it around in |
50
|
|
|
|
|
|
|
# an object, just do this: |
51
|
|
|
|
|
|
|
use Memoize; |
52
|
|
|
|
|
|
|
memoize('new_trafo'); |
53
|
|
|
|
|
|
|
# Then, passing the same transformation strings will result in a speedup of |
54
|
|
|
|
|
|
|
# about a factor 130 (on my machine) as compared to complete recreation |
55
|
|
|
|
|
|
|
# from strings. This is only 20% slower than using an existing |
56
|
|
|
|
|
|
|
# transformation. |
57
|
|
|
|
|
|
|
|
58
|
|
|
|
|
|
|
=head1 DESCRIPTION |
59
|
|
|
|
|
|
|
|
60
|
|
|
|
|
|
|
Math::Symbolic::Custom::Transformation is an extension to the Math::Symbolic |
61
|
|
|
|
|
|
|
module. You're assumed to be remotely familiar with that module throughout |
62
|
|
|
|
|
|
|
the documentation. |
63
|
|
|
|
|
|
|
|
64
|
|
|
|
|
|
|
This package implements transformations of Math::Symbolic trees using |
65
|
|
|
|
|
|
|
Math::Symbolic trees. I'll try to explain what this means in the following |
66
|
|
|
|
|
|
|
paragraphs. |
67
|
|
|
|
|
|
|
|
68
|
|
|
|
|
|
|
Until now, in order to be able to inspect a Math::Symbolic tree, one had to |
69
|
|
|
|
|
|
|
use the low-level Math::Symbolic interface like comparing the top node's |
70
|
|
|
|
|
|
|
term type with a constant (such as C) and then its operator type |
71
|
|
|
|
|
|
|
with more constants. This has changed with the release of |
72
|
|
|
|
|
|
|
Math::Symbolic::Custom::Pattern. |
73
|
|
|
|
|
|
|
|
74
|
|
|
|
|
|
|
To modify the tree, you had to use equally low-level or even |
75
|
|
|
|
|
|
|
encapsulation-breaking methods. This is meant to be changed by this |
76
|
|
|
|
|
|
|
distribution. |
77
|
|
|
|
|
|
|
|
78
|
|
|
|
|
|
|
=head2 EXAMPLE |
79
|
|
|
|
|
|
|
|
80
|
|
|
|
|
|
|
Say you want to change any tree that is a sum of two identical |
81
|
|
|
|
|
|
|
trees into two times one such tree. Let's assume the original object is in |
82
|
|
|
|
|
|
|
the variable C<$tree>. The old way was: (strictures and warnings assumed) |
83
|
|
|
|
|
|
|
|
84
|
|
|
|
|
|
|
use Math::Symbolic qw/:all/; |
85
|
|
|
|
|
|
|
|
86
|
|
|
|
|
|
|
sub sum_to_product { |
87
|
|
|
|
|
|
|
if ( $tree->term_type() == T_OPERATOR |
88
|
|
|
|
|
|
|
and $tree->type() == B_SUM |
89
|
|
|
|
|
|
|
and $tree->op1()->is_identical($tree->op2()) ) |
90
|
|
|
|
|
|
|
{ |
91
|
|
|
|
|
|
|
$tree = Math::Symbolic::Operator->new( |
92
|
|
|
|
|
|
|
'*', Math::Symbolic::Constant->new(2), $tree->op1()->new() |
93
|
|
|
|
|
|
|
); |
94
|
|
|
|
|
|
|
} |
95
|
|
|
|
|
|
|
return $tree; |
96
|
|
|
|
|
|
|
} |
97
|
|
|
|
|
|
|
|
98
|
|
|
|
|
|
|
What you'd do with this package is significantly more readable: |
99
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
use Math::Symbolic::Custom::Transformation qw/new_trafo/; |
101
|
|
|
|
|
|
|
|
102
|
|
|
|
|
|
|
my $Sum_To_Product_Rule = new_trafo('TREE_a + TREE_a' => '2 * TREE_a'); |
103
|
|
|
|
|
|
|
|
104
|
|
|
|
|
|
|
sub sum_to_product { |
105
|
|
|
|
|
|
|
my $tree = shift; |
106
|
|
|
|
|
|
|
return( $Sum_To_Product_Rule->apply($tree) || $tree ); |
107
|
|
|
|
|
|
|
} |
108
|
|
|
|
|
|
|
|
109
|
|
|
|
|
|
|
Either version could be shortened, of course. The significant improvement, |
110
|
|
|
|
|
|
|
however, isn't shown by this example. If you're doing introspection beyond |
111
|
|
|
|
|
|
|
the outermost operator, you will end up with giant, hardly readable |
112
|
|
|
|
|
|
|
if-else blocks when using the old style transformations. With this package, |
113
|
|
|
|
|
|
|
however, such introspection scales well: |
114
|
|
|
|
|
|
|
|
115
|
|
|
|
|
|
|
use Math::Symbolic::Custom::Transformation qw/new_trafo/; |
116
|
|
|
|
|
|
|
|
117
|
|
|
|
|
|
|
my $Sum_Of_Const_Products_Rule = new_trafo( |
118
|
|
|
|
|
|
|
'CONST_a * TREE_b + CONST_c * TREE_b' |
119
|
|
|
|
|
|
|
=> 'value{CONST_a + CONST_c} * TREE_b' |
120
|
|
|
|
|
|
|
); |
121
|
|
|
|
|
|
|
|
122
|
|
|
|
|
|
|
sub sum_to_product { |
123
|
|
|
|
|
|
|
my $tree = shift; |
124
|
|
|
|
|
|
|
return( $Sum_Of_Const_Products_Rule->apply($tree) || $tree ); |
125
|
|
|
|
|
|
|
} |
126
|
|
|
|
|
|
|
|
127
|
|
|
|
|
|
|
For details on the C construct in the transformation string, see |
128
|
|
|
|
|
|
|
the L section. |
129
|
|
|
|
|
|
|
|
130
|
|
|
|
|
|
|
=head2 EXPORT |
131
|
|
|
|
|
|
|
|
132
|
|
|
|
|
|
|
None by default, but you may choose to import the C subroutine |
133
|
|
|
|
|
|
|
as an alternative constructor for Math::Symbolic::Custom::Transformation |
134
|
|
|
|
|
|
|
objects. |
135
|
|
|
|
|
|
|
|
136
|
|
|
|
|
|
|
=head2 PERFORMANCE |
137
|
|
|
|
|
|
|
|
138
|
|
|
|
|
|
|
The performance of transformations isn't astonishing by itself, but if you |
139
|
|
|
|
|
|
|
take into account that they leave the original tree intact, we end up with |
140
|
|
|
|
|
|
|
a speed hit of only 16% as compared to the literal code. (That's the |
141
|
|
|
|
|
|
|
huge if-else block I was talking about.) |
142
|
|
|
|
|
|
|
|
143
|
|
|
|
|
|
|
You may be tempted to recreate the transformation objects from strings |
144
|
|
|
|
|
|
|
whenever you need them. There's one thing to say about that: Don't! |
145
|
|
|
|
|
|
|
The construction of transformations is really slow because they have |
146
|
|
|
|
|
|
|
been optimised for performance on application, not creation. |
147
|
|
|
|
|
|
|
(Application should be around 40 times faster than creation from strings!) |
148
|
|
|
|
|
|
|
|
149
|
|
|
|
|
|
|
I Starting with version 2.00, this module also supports the new-ish |
150
|
|
|
|
|
|
|
Math::Symbolic::Parser::Yapp parser implementation which is significantly |
151
|
|
|
|
|
|
|
faster than the old Parse::RecDescent based implementation. Replacement |
152
|
|
|
|
|
|
|
strings are parsed using Yapp by default now, which means a performance |
153
|
|
|
|
|
|
|
increase of about 20%. The search patterns are still parsed using the default |
154
|
|
|
|
|
|
|
Math::Symbolic parser which will be switched to Yapp at some point in the |
155
|
|
|
|
|
|
|
future. If you force the use of the Yapp parser globally, the parser |
156
|
|
|
|
|
|
|
performance will improve by about an order of magnitude! You can do so by |
157
|
|
|
|
|
|
|
adding the following before using Math::Symbolic::Custom::Transformation: |
158
|
|
|
|
|
|
|
|
159
|
|
|
|
|
|
|
use Math::Symbolic; |
160
|
|
|
|
|
|
|
BEGIN { |
161
|
|
|
|
|
|
|
$Math::Symbolic::Parser = Math::Symbolic::Parser->new( |
162
|
|
|
|
|
|
|
implementation => 'Yapp' |
163
|
|
|
|
|
|
|
); |
164
|
|
|
|
|
|
|
} |
165
|
|
|
|
|
|
|
use Math::Symbolic::Custom::Transformation; |
166
|
|
|
|
|
|
|
#... |
167
|
|
|
|
|
|
|
|
168
|
|
|
|
|
|
|
If you absolutely must include the source strings where the transformation |
169
|
|
|
|
|
|
|
is used, consider using the L module which is part of the standard |
170
|
|
|
|
|
|
|
Perl distribution these days. |
171
|
|
|
|
|
|
|
|
172
|
|
|
|
|
|
|
use Memoize; |
173
|
|
|
|
|
|
|
use Math::Symbolic::Custom::Transformation qw/new_trafo/; |
174
|
|
|
|
|
|
|
memoize('new_trafo'); |
175
|
|
|
|
|
|
|
|
176
|
|
|
|
|
|
|
sub apply_some_trafo { |
177
|
|
|
|
|
|
|
my $source = shift; |
178
|
|
|
|
|
|
|
my $trafo = new_trafo(...some pattern... => ...some transformation...); |
179
|
|
|
|
|
|
|
return $trafo->apply($source); |
180
|
|
|
|
|
|
|
} |
181
|
|
|
|
|
|
|
|
182
|
|
|
|
|
|
|
This usage has the advantage of putting the transformation source strings |
183
|
|
|
|
|
|
|
right where they make the most sense in terms of readability. The |
184
|
|
|
|
|
|
|
memoized subroutine C only constructs the transformation the first |
185
|
|
|
|
|
|
|
time it is called and returns the cached object every time thereafter. |
186
|
|
|
|
|
|
|
|
187
|
|
|
|
|
|
|
=head2 SYNTAX EXTENSIONS |
188
|
|
|
|
|
|
|
|
189
|
|
|
|
|
|
|
The strings from which you can create transformations are basically those that |
190
|
|
|
|
|
|
|
can be parsed as Math::Symbolic trees. The first argument to the transformation |
191
|
|
|
|
|
|
|
constructor will, in fact, be parsed as a Math::Symbolic::Custom::Pattern |
192
|
|
|
|
|
|
|
object. The second, however, may include some extensions to the default |
193
|
|
|
|
|
|
|
Math::Symbolic syntax. These extensions are the two functions C |
194
|
|
|
|
|
|
|
and C. The curly braces serve the purpose to show the |
195
|
|
|
|
|
|
|
distinction from algebraic parenthesis. When finding a C |
196
|
|
|
|
|
|
|
directive, the module will calculate the value of C when the |
197
|
|
|
|
|
|
|
transformation is applied. (That is, after the C, C and |
198
|
|
|
|
|
|
|
C placeholders have been inserted!) The result is then inserted |
199
|
|
|
|
|
|
|
into the transformed tree. |
200
|
|
|
|
|
|
|
|
201
|
|
|
|
|
|
|
Similarily, the C directive will use the Math::Symbolic |
202
|
|
|
|
|
|
|
simplification routines on C when the transformation is being applied |
203
|
|
|
|
|
|
|
(and again, after replacing the placeholders with the matched sub-trees. |
204
|
|
|
|
|
|
|
|
205
|
|
|
|
|
|
|
=cut |
206
|
|
|
|
|
|
|
|
207
|
|
|
|
|
|
|
our %EXPORT_TAGS = ( 'all' => [ qw( |
208
|
|
|
|
|
|
|
new_trafo new_trafo_group |
209
|
|
|
|
|
|
|
) ] ); |
210
|
|
|
|
|
|
|
|
211
|
|
|
|
|
|
|
our @EXPORT_OK = ( @{ $EXPORT_TAGS{'all'} } ); |
212
|
|
|
|
|
|
|
|
213
|
|
|
|
|
|
|
our @EXPORT = qw(); |
214
|
|
|
|
|
|
|
|
215
|
|
|
|
|
|
|
our $Predicates = [ |
216
|
|
|
|
|
|
|
qw/simplify value/ |
217
|
|
|
|
|
|
|
]; |
218
|
|
|
|
|
|
|
|
219
|
|
|
|
|
|
|
# We have some class data. Namely, the parser for the transformation strings |
220
|
|
|
|
|
|
|
# which aren't quite ordinary Math::Symbolic strings. |
221
|
|
|
|
|
|
|
our $Parser; |
222
|
|
|
|
|
|
|
{ |
223
|
|
|
|
|
|
|
my $pred = join '|', @$Predicates; |
224
|
|
|
|
|
|
|
$Parser = Math::Symbolic::Parser->new( |
225
|
|
|
|
|
|
|
implementation => 'Yapp', |
226
|
|
|
|
|
|
|
yapp_predicates => qr/$pred/o, |
227
|
|
|
|
|
|
|
); |
228
|
|
|
|
|
|
|
} |
229
|
|
|
|
|
|
|
|
230
|
|
|
|
|
|
|
if ($Parser->isa('Parse::RecDescent')) { |
231
|
|
|
|
|
|
|
# This is left in for reference. |
232
|
|
|
|
|
|
|
my $pred = join '|', @$Predicates; |
233
|
|
|
|
|
|
|
$Parser->Extend(<<"HERE"); |
234
|
|
|
|
|
|
|
function: /(?:$pred)\{/ expr '}' |
235
|
|
|
|
|
|
|
{ |
236
|
|
|
|
|
|
|
my \$function_name = \$item[1]; |
237
|
|
|
|
|
|
|
\$function_name =~ s/\{\$//; |
238
|
|
|
|
|
|
|
|
239
|
|
|
|
|
|
|
my \$inner = \$item[2]; |
240
|
|
|
|
|
|
|
|
241
|
|
|
|
|
|
|
my \$name = 'TRANSFORMATION_HOOK'; |
242
|
|
|
|
|
|
|
|
243
|
|
|
|
|
|
|
# Since we need to evaluate both 'simplify' and 'value' |
244
|
|
|
|
|
|
|
# at the time we apply the transformation, we just replace |
245
|
|
|
|
|
|
|
# the function occurrance with a special variable that is |
246
|
|
|
|
|
|
|
# recognized later. The function name and argument is stored |
247
|
|
|
|
|
|
|
# in an array as the value of the special variable. |
248
|
|
|
|
|
|
|
Math::Symbolic::Variable->new( |
249
|
|
|
|
|
|
|
\$name, [\$function_name, \$inner] |
250
|
|
|
|
|
|
|
); |
251
|
|
|
|
|
|
|
} |
252
|
|
|
|
|
|
|
HERE |
253
|
|
|
|
|
|
|
} |
254
|
|
|
|
|
|
|
elsif ($Parser->isa('Math::Symbolic::Parser::Yapp')) { |
255
|
|
|
|
|
|
|
# This is a no-op since the logic had to be built into |
256
|
|
|
|
|
|
|
# the Yapp parser. *sigh* |
257
|
|
|
|
|
|
|
} |
258
|
|
|
|
|
|
|
else { |
259
|
|
|
|
|
|
|
die "Unsupported Math::Symbolic::Parser implementation."; |
260
|
|
|
|
|
|
|
} |
261
|
|
|
|
|
|
|
|
262
|
|
|
|
|
|
|
=head2 METHODS |
263
|
|
|
|
|
|
|
|
264
|
|
|
|
|
|
|
This is a list of public methods. |
265
|
|
|
|
|
|
|
|
266
|
|
|
|
|
|
|
=over 2 |
267
|
|
|
|
|
|
|
|
268
|
|
|
|
|
|
|
=cut |
269
|
|
|
|
|
|
|
|
270
|
|
|
|
|
|
|
=item new |
271
|
|
|
|
|
|
|
|
272
|
|
|
|
|
|
|
This is the constructor for Math::Symbolic::Custom::Transformation objects. |
273
|
|
|
|
|
|
|
It takes two arguments: A pattern to look for and a replacement. |
274
|
|
|
|
|
|
|
|
275
|
|
|
|
|
|
|
The pattern may either be a Math::Symbolic::Custom::Pattern object (fastest), |
276
|
|
|
|
|
|
|
or a Math::Symbolic tree which will internally be transformed into a pattern |
277
|
|
|
|
|
|
|
or even just a string which will be parsed as a pattern. |
278
|
|
|
|
|
|
|
|
279
|
|
|
|
|
|
|
The replacement for the pattern may either be a Math::Symbolic tree or a |
280
|
|
|
|
|
|
|
string to be parsed as such. |
281
|
|
|
|
|
|
|
|
282
|
|
|
|
|
|
|
=cut |
283
|
|
|
|
|
|
|
|
284
|
|
|
|
|
|
|
sub new { |
285
|
114
|
|
|
114
|
1
|
61970
|
my $proto = shift; |
286
|
114
|
|
33
|
|
|
656
|
my $class = ref($proto)||$proto; |
287
|
|
|
|
|
|
|
|
288
|
114
|
|
|
|
|
207
|
my $pattern = shift; |
289
|
114
|
|
|
|
|
225
|
my $replacement = shift; |
290
|
|
|
|
|
|
|
|
291
|
|
|
|
|
|
|
# parameter checking |
292
|
114
|
50
|
33
|
|
|
556
|
if (not defined $pattern or not defined $replacement) { |
293
|
0
|
|
|
|
|
0
|
croak("Arguments to ".__PACKAGE__."->new() must be a valid pattern and a replacement for matched patterns."); |
294
|
|
|
|
|
|
|
} |
295
|
|
|
|
|
|
|
|
296
|
114
|
50
|
|
|
|
287
|
if (not ref($pattern)) { |
297
|
114
|
|
|
|
|
215
|
my $copy = $pattern; |
298
|
114
|
|
|
|
|
484
|
$pattern = parse_from_string($pattern); |
299
|
114
|
100
|
|
|
|
1447846
|
if (not ref($pattern)) { |
300
|
2
|
|
|
|
|
425
|
croak("Failed to parse pattern '$copy' as a Math::Symbolic tree."); |
301
|
|
|
|
|
|
|
} |
302
|
|
|
|
|
|
|
} |
303
|
|
|
|
|
|
|
|
304
|
112
|
50
|
|
|
|
1730
|
if (not $pattern->isa('Math::Symbolic::Custom::Pattern')) { |
305
|
112
|
|
|
|
|
198
|
eval {$pattern = Math::Symbolic::Custom::Pattern->new($pattern);}; |
|
112
|
|
|
|
|
748
|
|
306
|
112
|
50
|
33
|
|
|
30590
|
if ( $@ or not ref($pattern) |
|
|
|
33
|
|
|
|
|
307
|
|
|
|
|
|
|
or not $pattern->isa('Math::Symbolic::Custom::Pattern') ) |
308
|
|
|
|
|
|
|
{ |
309
|
0
|
0
|
|
|
|
0
|
croak( |
310
|
|
|
|
|
|
|
"Could not transform pattern source into a pattern object." |
311
|
|
|
|
|
|
|
. ($@?" Error: $@":"") |
312
|
|
|
|
|
|
|
); |
313
|
|
|
|
|
|
|
} |
314
|
|
|
|
|
|
|
} |
315
|
|
|
|
|
|
|
|
316
|
112
|
50
|
|
|
|
413
|
if (not ref($replacement) =~ /^Math::Symbolic/) { |
317
|
112
|
|
|
|
|
205
|
my $copy = $replacement; |
318
|
112
|
|
|
|
|
988
|
$replacement = $Parser->parse($replacement); |
319
|
112
|
50
|
|
|
|
56535
|
if (not ref($replacement) =~ /^Math::Symbolic/) { |
320
|
0
|
|
|
|
|
0
|
croak( |
321
|
|
|
|
|
|
|
"Failed to parse replacement '$copy' as a Math::Symbolic tree." |
322
|
|
|
|
|
|
|
); |
323
|
|
|
|
|
|
|
} |
324
|
|
|
|
|
|
|
} |
325
|
|
|
|
|
|
|
|
326
|
112
|
|
|
|
|
439
|
my $self = { |
327
|
|
|
|
|
|
|
pattern => $pattern, |
328
|
|
|
|
|
|
|
replacement => $replacement, |
329
|
|
|
|
|
|
|
}; |
330
|
|
|
|
|
|
|
|
331
|
112
|
|
|
|
|
447
|
bless $self => $class; |
332
|
|
|
|
|
|
|
|
333
|
112
|
|
|
|
|
479
|
return $self; |
334
|
|
|
|
|
|
|
} |
335
|
|
|
|
|
|
|
|
336
|
|
|
|
|
|
|
|
337
|
|
|
|
|
|
|
=item apply |
338
|
|
|
|
|
|
|
|
339
|
|
|
|
|
|
|
Applies the transformation to a Math::Symbolic tree. First argument must be |
340
|
|
|
|
|
|
|
a Math::Symbolic tree to transform. The tree is not transformed in-place, |
341
|
|
|
|
|
|
|
but its matched subtrees are contained in the transformed tree, so if you plan |
342
|
|
|
|
|
|
|
to use the original tree as well as the transformed tree, take |
343
|
|
|
|
|
|
|
care to clone one of the trees. |
344
|
|
|
|
|
|
|
|
345
|
|
|
|
|
|
|
C returns the transformed tree if the transformation pattern matched |
346
|
|
|
|
|
|
|
and a false value otherwise. |
347
|
|
|
|
|
|
|
|
348
|
|
|
|
|
|
|
On errors, it throws a fatal error. |
349
|
|
|
|
|
|
|
|
350
|
|
|
|
|
|
|
=cut |
351
|
|
|
|
|
|
|
|
352
|
|
|
|
|
|
|
sub apply { |
353
|
138
|
|
|
138
|
1
|
2161590
|
my $self = shift; |
354
|
138
|
|
|
|
|
340
|
my $tree = shift; |
355
|
|
|
|
|
|
|
|
356
|
138
|
50
|
|
|
|
960
|
if (not ref($tree) =~ /^Math::Symbolic/) { |
357
|
0
|
|
|
|
|
0
|
croak("First argument to apply() must be a Math::Symbolic tree."); |
358
|
|
|
|
|
|
|
} |
359
|
|
|
|
|
|
|
|
360
|
138
|
|
|
|
|
374
|
my $pattern = $self->{pattern}; |
361
|
138
|
|
|
|
|
321
|
my $repl = $self->{replacement}; |
362
|
|
|
|
|
|
|
|
363
|
138
|
|
|
|
|
813
|
my $matched = $pattern->match($tree); |
364
|
|
|
|
|
|
|
|
365
|
138
|
100
|
|
|
|
22222
|
return undef if not $matched; |
366
|
|
|
|
|
|
|
|
367
|
72
|
|
|
|
|
164
|
my $match_vars = $matched->{vars}; |
368
|
72
|
|
|
|
|
161
|
my $match_trees = $matched->{trees}; |
369
|
72
|
|
|
|
|
203
|
my $match_consts = $matched->{constants}; |
370
|
|
|
|
|
|
|
|
371
|
72
|
|
|
|
|
310
|
my $new = $repl->new(); |
372
|
|
|
|
|
|
|
|
373
|
1
|
|
|
1
|
|
11
|
no warnings 'recursion'; |
|
1
|
|
|
|
|
3
|
|
|
1
|
|
|
|
|
1303
|
|
374
|
|
|
|
|
|
|
|
375
|
72
|
|
|
|
|
3388
|
my $subroutine; |
376
|
|
|
|
|
|
|
my @descend_options; |
377
|
|
|
|
|
|
|
|
378
|
|
|
|
|
|
|
$subroutine = sub { |
379
|
185
|
|
|
185
|
|
4435
|
my $tree = shift; |
380
|
185
|
100
|
|
|
|
515
|
if ($tree->term_type() == T_VARIABLE) { |
381
|
105
|
|
|
|
|
545
|
my $name = $tree->{name}; |
382
|
105
|
100
|
|
|
|
674
|
if ($name eq 'TRANSFORMATION_HOOK') { |
|
|
100
|
|
|
|
|
|
383
|
|
|
|
|
|
|
|
384
|
14
|
|
|
|
|
56
|
my $hook = $tree->value(); |
385
|
14
|
50
|
33
|
|
|
157
|
if (not ref($hook) eq 'ARRAY' and @$hook == 2) { |
386
|
0
|
|
|
|
|
0
|
croak("Found invalid transformation hook in replacement tree. Did you use a variable named 'TRANSFORMATION_HOOK'? If so, please change its name since that name is used internally."); |
387
|
|
|
|
|
|
|
} |
388
|
|
|
|
|
|
|
else { |
389
|
14
|
|
|
|
|
29
|
my $type = $hook->[0]; |
390
|
14
|
|
|
|
|
51
|
my $operand = $hook->[1]->new(); |
391
|
14
|
|
|
|
|
948
|
$operand->descend( |
392
|
|
|
|
|
|
|
@descend_options |
393
|
|
|
|
|
|
|
); |
394
|
|
|
|
|
|
|
|
395
|
14
|
100
|
|
|
|
421
|
if ($type eq 'simplify') { |
|
|
50
|
|
|
|
|
|
396
|
13
|
|
|
|
|
57
|
my $simplified = $operand->simplify(); |
397
|
13
|
|
|
|
|
6632
|
$tree->replace($simplified); |
398
|
13
|
|
|
|
|
209
|
return undef; |
399
|
|
|
|
|
|
|
} |
400
|
|
|
|
|
|
|
elsif ($type eq 'value') { |
401
|
1
|
|
|
|
|
5
|
my $value = $operand->value(); |
402
|
1
|
50
|
|
|
|
206
|
if (not defined $value) { |
403
|
0
|
|
|
|
|
0
|
croak("Tried to evaluate transformation subroutine value() but it evaluated to an undefined value."); |
404
|
|
|
|
|
|
|
} |
405
|
1
|
|
|
|
|
4
|
$value = Math::Symbolic::Constant->new($value); |
406
|
1
|
|
|
|
|
19
|
$tree->replace($value); |
407
|
1
|
|
|
|
|
15
|
return undef; |
408
|
|
|
|
|
|
|
} |
409
|
|
|
|
|
|
|
else { |
410
|
0
|
|
|
|
|
0
|
die("Invalid TRANSFORMATION_HOOK type '$type'."); |
411
|
|
|
|
|
|
|
} |
412
|
|
|
|
|
|
|
} |
413
|
|
|
|
|
|
|
} |
414
|
|
|
|
|
|
|
elsif ($name =~ /^(VAR|CONST|TREE)_(\w+)/) { |
415
|
90
|
|
|
|
|
206
|
my $type = $1; |
416
|
90
|
|
|
|
|
194
|
my $name = $2; |
417
|
90
|
50
|
|
|
|
253
|
if ($type eq 'VAR') { |
|
|
100
|
|
|
|
|
|
418
|
0
|
0
|
|
|
|
0
|
if (exists $match_vars->{$name}) { |
419
|
0
|
|
|
|
|
0
|
$tree->replace( |
420
|
|
|
|
|
|
|
Math::Symbolic::Variable->new( |
421
|
|
|
|
|
|
|
$match_vars->{$name} |
422
|
|
|
|
|
|
|
) |
423
|
|
|
|
|
|
|
); |
424
|
|
|
|
|
|
|
} |
425
|
|
|
|
|
|
|
} |
426
|
|
|
|
|
|
|
elsif ($type eq 'TREE') { |
427
|
86
|
50
|
|
|
|
234
|
if (exists $match_trees->{$name}) { |
428
|
86
|
|
|
|
|
313
|
$tree->replace($match_trees->{$name}); |
429
|
|
|
|
|
|
|
} |
430
|
|
|
|
|
|
|
} |
431
|
|
|
|
|
|
|
else { |
432
|
4
|
50
|
|
|
|
15
|
if (exists $match_consts->{$name}) { |
433
|
4
|
|
|
|
|
22
|
$tree->replace( |
434
|
|
|
|
|
|
|
Math::Symbolic::Constant->new( |
435
|
|
|
|
|
|
|
$match_consts->{$name} |
436
|
|
|
|
|
|
|
) |
437
|
|
|
|
|
|
|
); |
438
|
|
|
|
|
|
|
} |
439
|
|
|
|
|
|
|
} |
440
|
|
|
|
|
|
|
|
441
|
90
|
|
|
|
|
1301
|
return undef; |
442
|
|
|
|
|
|
|
} |
443
|
1
|
|
|
|
|
4
|
return(); |
444
|
|
|
|
|
|
|
} |
445
|
|
|
|
|
|
|
else { |
446
|
80
|
|
|
|
|
469
|
return(); |
447
|
|
|
|
|
|
|
} |
448
|
72
|
|
|
|
|
684
|
}; |
449
|
|
|
|
|
|
|
@descend_options = ( |
450
|
|
|
|
|
|
|
in_place => 1, |
451
|
|
|
|
|
|
|
operand_finder => sub { |
452
|
52
|
50
|
|
52
|
|
1313
|
if ($_[0]->term_type == T_OPERATOR) { |
453
|
52
|
|
|
|
|
247
|
return @{$_[0]->{operands}}; |
|
52
|
|
|
|
|
243
|
|
454
|
|
|
|
|
|
|
} |
455
|
|
|
|
|
|
|
else { |
456
|
0
|
|
|
|
|
0
|
return(); |
457
|
|
|
|
|
|
|
} |
458
|
|
|
|
|
|
|
}, |
459
|
72
|
|
|
|
|
502
|
before => $subroutine, |
460
|
|
|
|
|
|
|
); |
461
|
72
|
|
|
|
|
487
|
$new->descend(@descend_options); |
462
|
72
|
|
|
|
|
2113
|
return $new; |
463
|
|
|
|
|
|
|
} |
464
|
|
|
|
|
|
|
|
465
|
|
|
|
|
|
|
=item apply_recursive |
466
|
|
|
|
|
|
|
|
467
|
|
|
|
|
|
|
"Recursively" applies the transformation. The Math::Symbolic tree |
468
|
|
|
|
|
|
|
passed in as argument B. |
469
|
|
|
|
|
|
|
|
470
|
|
|
|
|
|
|
Hold on: This does not mean |
471
|
|
|
|
|
|
|
that the transformation is applied again and again, but that the |
472
|
|
|
|
|
|
|
Math::Symbolic tree you are applying to is descended into and while walking |
473
|
|
|
|
|
|
|
back up the tree, the transformation is tried for every node. |
474
|
|
|
|
|
|
|
|
475
|
|
|
|
|
|
|
Basically, it's applied bottom-up. Top-down would not usually make much sense. |
476
|
|
|
|
|
|
|
If the application to any sub-tree throws a fatal error, this error is silently |
477
|
|
|
|
|
|
|
caught and the application to other sub-trees is continued. |
478
|
|
|
|
|
|
|
|
479
|
|
|
|
|
|
|
Usage is the same as with the "shallow" C method. |
480
|
|
|
|
|
|
|
|
481
|
|
|
|
|
|
|
=cut |
482
|
|
|
|
|
|
|
|
483
|
|
|
|
|
|
|
sub apply_recursive { |
484
|
0
|
|
|
0
|
1
|
0
|
my $self = shift; |
485
|
0
|
|
|
|
|
0
|
my $tree = shift; |
486
|
|
|
|
|
|
|
|
487
|
0
|
|
|
|
|
0
|
my $matched = 0; |
488
|
|
|
|
|
|
|
$tree->descend( |
489
|
|
|
|
|
|
|
after => sub { |
490
|
0
|
|
|
0
|
|
0
|
my $node = shift; |
491
|
0
|
|
|
|
|
0
|
my $res; |
492
|
0
|
|
|
|
|
0
|
eval { $res = $self->apply($node); }; |
|
0
|
|
|
|
|
0
|
|
493
|
0
|
0
|
0
|
|
|
0
|
if (defined $res and not $@) { |
494
|
0
|
|
|
|
|
0
|
$matched = 1; |
495
|
0
|
|
|
|
|
0
|
$node->replace($res); |
496
|
|
|
|
|
|
|
} |
497
|
0
|
|
|
|
|
0
|
return(); |
498
|
|
|
|
|
|
|
}, |
499
|
0
|
|
|
|
|
0
|
in_place => 1 |
500
|
|
|
|
|
|
|
); |
501
|
|
|
|
|
|
|
|
502
|
0
|
0
|
|
|
|
0
|
return $tree if $matched; |
503
|
0
|
|
|
|
|
0
|
return(); |
504
|
|
|
|
|
|
|
} |
505
|
|
|
|
|
|
|
|
506
|
|
|
|
|
|
|
=item to_string |
507
|
|
|
|
|
|
|
|
508
|
|
|
|
|
|
|
Returns a string representation of the transformation. |
509
|
|
|
|
|
|
|
In presence of the C or C hooks, this may |
510
|
|
|
|
|
|
|
fail to return the correct represenation. It does not round-trip! |
511
|
|
|
|
|
|
|
|
512
|
|
|
|
|
|
|
(Generally, it should work if only one hook is present, but fails if |
513
|
|
|
|
|
|
|
more than one hook is found.) |
514
|
|
|
|
|
|
|
|
515
|
|
|
|
|
|
|
=cut |
516
|
|
|
|
|
|
|
|
517
|
|
|
|
|
|
|
sub to_string { |
518
|
56
|
|
|
56
|
1
|
94268
|
my $self = shift; |
519
|
56
|
|
|
|
|
366
|
my $pattern_str = $self->{pattern}->to_string(); |
520
|
56
|
|
|
|
|
348
|
my $repl = $self->{replacement}; |
521
|
|
|
|
|
|
|
|
522
|
56
|
|
|
|
|
195
|
my $repl_str = _repl_to_string($repl); |
523
|
|
|
|
|
|
|
|
524
|
56
|
|
|
|
|
267
|
return $pattern_str . ' -> ' . $repl_str; |
525
|
|
|
|
|
|
|
} |
526
|
|
|
|
|
|
|
|
527
|
|
|
|
|
|
|
sub _repl_to_string { |
528
|
67
|
|
|
67
|
|
122
|
my $repl = shift; |
529
|
67
|
|
|
|
|
290
|
my $repl_str = $repl->to_string(); |
530
|
67
|
100
|
|
|
|
2265
|
if ($repl_str =~ /TRANSFORMATION_HOOK/) { |
531
|
11
|
|
|
|
|
21
|
my @hooks; |
532
|
|
|
|
|
|
|
$repl->descend( |
533
|
|
|
|
|
|
|
before => sub { |
534
|
33
|
|
|
33
|
|
1261
|
my $node = shift; |
535
|
33
|
100
|
100
|
|
|
196
|
if ( |
536
|
|
|
|
|
|
|
ref($node) =~ /^Math::Symbolic::Variable$/ |
537
|
|
|
|
|
|
|
and $node->name() eq 'TRANSFORMATION_HOOK' |
538
|
|
|
|
|
|
|
) |
539
|
|
|
|
|
|
|
{ |
540
|
11
|
|
|
|
|
98
|
push @hooks, $node; |
541
|
|
|
|
|
|
|
} |
542
|
33
|
|
|
|
|
174
|
return(); |
543
|
|
|
|
|
|
|
}, |
544
|
11
|
|
|
|
|
121
|
in_place => 1, # won't change anything |
545
|
|
|
|
|
|
|
); |
546
|
|
|
|
|
|
|
|
547
|
11
|
|
|
|
|
386
|
$repl_str =~ s{TRANSFORMATION_HOOK}! |
548
|
11
|
|
|
|
|
24
|
my $node = shift @hooks; |
549
|
11
|
|
|
|
|
48
|
my $value = $node->value(); |
550
|
11
|
|
|
|
|
97
|
my $operand = _repl_to_string($value->[1]); |
551
|
11
|
|
|
|
|
25
|
my $name = $value->[0]; |
552
|
11
|
|
|
|
|
60
|
"$name\{ $operand }" |
553
|
|
|
|
|
|
|
!ge; |
554
|
|
|
|
|
|
|
} |
555
|
|
|
|
|
|
|
|
556
|
67
|
|
|
|
|
182
|
return $repl_str; |
557
|
|
|
|
|
|
|
} |
558
|
|
|
|
|
|
|
|
559
|
|
|
|
|
|
|
=back |
560
|
|
|
|
|
|
|
|
561
|
|
|
|
|
|
|
=head2 SUBROUTINES |
562
|
|
|
|
|
|
|
|
563
|
|
|
|
|
|
|
This is a list of public subroutines. |
564
|
|
|
|
|
|
|
|
565
|
|
|
|
|
|
|
=over 2 |
566
|
|
|
|
|
|
|
|
567
|
|
|
|
|
|
|
=cut |
568
|
|
|
|
|
|
|
|
569
|
|
|
|
|
|
|
=item new_trafo |
570
|
|
|
|
|
|
|
|
571
|
|
|
|
|
|
|
This subroutine is an alternative to the C constructor for |
572
|
|
|
|
|
|
|
Math::Symbolic::Custom::Transformation objects that uses a hard coded |
573
|
|
|
|
|
|
|
package name. (So if you want to subclass this module, you should be aware |
574
|
|
|
|
|
|
|
of that!) |
575
|
|
|
|
|
|
|
|
576
|
|
|
|
|
|
|
=cut |
577
|
|
|
|
|
|
|
|
578
|
|
|
|
|
|
|
=item new_trafo_group |
579
|
|
|
|
|
|
|
|
580
|
|
|
|
|
|
|
This subroutine is the equivalent of C, but for creation |
581
|
|
|
|
|
|
|
of new transformation groups. See L. |
582
|
|
|
|
|
|
|
|
583
|
|
|
|
|
|
|
=cut |
584
|
|
|
|
|
|
|
|
585
|
|
|
|
|
|
|
*new_trafo_group = *Math::Symbolic::Custom::Transformation::Group::new_trafo_group; |
586
|
|
|
|
|
|
|
|
587
|
|
|
|
|
|
|
sub new_trafo { |
588
|
57
|
|
|
57
|
1
|
564
|
unshift @_, __PACKAGE__; |
589
|
57
|
|
|
|
|
289
|
goto &new; |
590
|
|
|
|
|
|
|
} |
591
|
|
|
|
|
|
|
|
592
|
|
|
|
|
|
|
1; |
593
|
|
|
|
|
|
|
__END__ |