NAME
Math::Symbolic::Custom::Transformation - Transform Math::Symbolic trees
SYNOPSIS
use Math::Symbolic::Custom::Transformation;
my $trafo = Math::Symbolic::Custom::Transformation->new(
'TREE_x + TREE_x' => '2 * TREE_x'
);
my $modified = $trafo->apply($math_symbolic_tree);
if (defined $modified) {
print "Outermost operator is a sum of two identical trees.\n";
print "Transformed it into a product. ($modified)\n";
}
else {
print "Transformation could not be applied.\n";
}
# shortcut: new_trafo
use Math::Symbolic::Custom::Transformation qw/new_trafo/;
# use the value() function to have the transformation compute the value
# of the expression after the replacements. simplify{} works similar.
my $another_trafo = new_trafo(
'TREE_foo / CONST_bar' => 'value{1/CONST_bar} * TREE_foo'
);
# If you'll need the same transformation but don't want to keep it around in
# an object, just do this:
use Memoize;
memoize('new_trafo');
# Then, passing the same transformation strings will result in a speedup of
# about a factor 130 (on my machine) as compared to complete recreation
# from strings. This is only 20% slower than using an existing
# transformation.
DESCRIPTION
Math::Symbolic::Custom::Transformation is an extension to the Math::Symbolic module. You're assumed to be remotely familiar with that module throughout the documentation.
This package implements transformations of Math::Symbolic trees using Math::Symbolic trees. I'll try to explain what this means in the following paragraphs.
Until now, in order to be able to inspect a Math::Symbolic tree, one had to use the low-level Math::Symbolic interface like comparing the top node's term type with a constant (such as "T_OPERATOR") and then its operator type with more constants. This has changed with the release of Math::Symbolic::Custom::Pattern.
To modify the tree, you had to use equally low-level or even encapsulation-breaking methods. This is meant to be changed by this distribution.
EXAMPLE
Say you want to change any tree that is a sum of two identical trees
into two times one such tree. Let's assume the original object is in the
variable $tree. The old way was: (strictures and warnings assumed)
use Math::Symbolic qw/:all/;
sub sum_to_product {
if ( $tree->term_type() == T_OPERATOR
and $tree->type() == B_SUM
and $tree->op1()->is_identical($tree->op2()) )
{
$tree = Math::Symbolic::Operator->new(
'*', Math::Symbolic::Constant->new(2), $tree->op1()->new()
);
}
return $tree;
}
What you'd do with this package is significantly more readable:
use Math::Symbolic::Custom::Transformation qw/new_trafo/;
my $Sum_To_Product_Rule = new_trafo('TREE_a + TREE_a' => '2 * TREE_a');
sub sum_to_product {
my $tree = shift;
return( $Sum_To_Product_Rule->apply($tree) || $tree );
}
Either version could be shortened, of course. The significant improvement, however, isn't shown by this example. If you're doing introspection beyond the outermost operator, you will end up with giant, hardly readable if-else blocks when using the old style transformations. With this package, however, such introspection scales well:
use Math::Symbolic::Custom::Transformation qw/new_trafo/;
my $Sum_Of_Const_Products_Rule = new_trafo(
'CONST_a * TREE_b + CONST_c * TREE_b'
=> 'value{CONST_a + CONST_c} * TREE_b'
);
sub sum_to_product {
my $tree = shift;
return( $Sum_Of_Const_Products_Rule->apply($tree) || $tree );
}
For details on the "value{}" construct in the transformation string, see the "SYNTAX EXTENSIONS" section.
EXPORT
None by default, but you may choose to import the "new_trafo" subroutine
as an alternative constructor for Math::Symbolic::Custom::Transformation
objects.
PERFORMANCE
The performance of transformations isn't astonishing by itself, but if
you take into account that they leave the original tree intact, we end
up with a speed hit of only 16% as compared to the literal code. (That's
the huge if-else block I was talking about.)
You may be tempted to recreate the transformation objects from strings whenever you need them. There's one thing to say about that: Don't! The construction of transformations is really slow because they have been optimised for performance on application, not creation. (Application should be around 40 times faster than creation from strings!)
Note: Starting with version 2.00, this module also supports the new-ish Math::Symbolic::Parser::Yapp parser implementation which is significantly faster than the old Parse::RecDescent based implementation. Replacement strings are parsed using Yapp by default now, which means a performance increase of about 20%. The search patterns are still parsed using the default Math::Symbolic parser which will be switched to Yapp at some point in the future. If you force the use of the Yapp parser globally, the parser performance will improve by about an order of magnitude! You can do so by adding the following before using Math::Symbolic::Custom::Transformation:
use Math::Symbolic;
BEGIN {
$Math::Symbolic::Parser = Math::Symbolic::Parser->new(
implementation => 'Yapp'
);
}
use Math::Symbolic::Custom::Transformation;
#...
If you absolutely must include the source strings where the transformation is used, consider using the Memoize module which is part of the standard Perl distribution these days.
use Memoize;
use Math::Symbolic::Custom::Transformation qw/new_trafo/;
memoize('new_trafo');
sub apply_some_trafo {
my $source = shift;
my $trafo = new_trafo(...some pattern... => ...some transformation...);
return $trafo->apply($source);
}
This usage has the advantage of putting the transformation source strings right where they make the most sense in terms of readability. The memoized subroutine "new_trafo" only constructs the transformation the first time it is called and returns the cached object every time thereafter.
SYNTAX EXTENSIONS
The strings from which you can create transformations are basically
those that can be parsed as Math::Symbolic trees. The first argument to
the transformation constructor will, in fact, be parsed as a
Math::Symbolic::Custom::Pattern object. The second, however, may include
some extensions to the default Math::Symbolic syntax. These extensions
are the two functions "value{...}" and "simplify{...}". The curly braces
serve the purpose to show the distinction from algebraic parenthesis.
When finding a "value{EXPR}" directive, the module will calculate the
value of "EXPR" when the transformation is applied. (That is, after the
"TREE_foo", "CONST_bar" and "VAR_baz" placeholders have been inserted!)
The result is then inserted into the transformed tree.
Similarily, the "simplify{EXPR}" directive will use the Math::Symbolic simplification routines on "EXPR" when the transformation is being applied (and again, after replacing the placeholders with the matched sub-trees.
METHODS
This is a list of public methods.
new
This is the constructor for Math::Symbolic::Custom::Transformation
objects. It takes two arguments: A pattern to look for and a
replacement.
The pattern may either be a Math::Symbolic::Custom::Pattern object
(fastest), or a Math::Symbolic tree which will internally be
transformed into a pattern or even just a string which will be parsed
as a pattern.
The replacement for the pattern may either be a Math::Symbolic tree or
a string to be parsed as such.
apply
Applies the transformation to a Math::Symbolic tree. First argument
must be a Math::Symbolic tree to transform. The tree is not
transformed in-place, but its matched subtrees are contained in the
transformed tree, so if you plan to use the original tree as well as
the transformed tree, take care to clone one of the trees.
"apply()" returns the transformed tree if the transformation pattern
matched and a false value otherwise.
On errors, it throws a fatal error.
apply_recursive
"Recursively" applies the transformation. The Math::Symbolic tree
passed in as argument will be modified in-place.
Hold on: This does not mean that the transformation is applied again
and again, but that the Math::Symbolic tree you are applying to is
descended into and while walking back up the tree, the transformation
is tried for every node.
Basically, it's applied bottom-up. Top-down would not usually make
much sense. If the application to any sub-tree throws a fatal error,
this error is silently caught and the application to other sub-trees
is continued.
Usage is the same as with the "shallow" "apply()" method.
to_string
Returns a string representation of the transformation. In presence of
the "simplify" or "value" hooks, this may fail to return the correct
represenation. It does not round-trip!
(Generally, it should work if only one hook is present, but fails if
more than one hook is found.)
SUBROUTINES
This is a list of public subroutines.
new_trafo
This subroutine is an alternative to the "new()" constructor for
Math::Symbolic::Custom::Transformation objects that uses a hard coded
package name. (So if you want to subclass this module, you should be
aware of that!)
new_trafo_group
This subroutine is the equivalent of "new_trafo", but for creation of
new transformation groups. See
Math::Symbolic::Custom::Transformation::Group.
SEE ALSO
New versions of this module can be found on http://steffen-mueller.net or CPAN.
This module uses the Math::Symbolic framework for symbolic computations.
Math::Symbolic::Custom::Pattern implements the pattern matching routines.
AUTHOR
Steffen Müller, <symbolic-module at steffen-mueller dot net>
COPYRIGHT AND LICENSE
Copyright (C) 2006-2008 by Steffen Mueller
This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself, either Perl version 5.6.1 or, at your option, any later version of Perl 5 you may have available.