| line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
|
1
|
|
|
|
|
|
|
package Math::Symbolic::Custom::CollectSimplify; |
|
2
|
|
|
|
|
|
|
|
|
3
|
2
|
|
|
2
|
|
226791
|
use 5.006001; |
|
|
2
|
|
|
|
|
6
|
|
|
4
|
2
|
|
|
2
|
|
10
|
use strict; |
|
|
2
|
|
|
|
|
4
|
|
|
|
2
|
|
|
|
|
70
|
|
|
5
|
2
|
|
|
2
|
|
9
|
use warnings; |
|
|
2
|
|
|
|
|
5
|
|
|
|
2
|
|
|
|
|
127
|
|
|
6
|
2
|
|
|
2
|
|
8
|
no warnings 'recursion'; |
|
|
2
|
|
|
|
|
4
|
|
|
|
2
|
|
|
|
|
113
|
|
|
7
|
|
|
|
|
|
|
|
|
8
|
|
|
|
|
|
|
=pod |
|
9
|
|
|
|
|
|
|
|
|
10
|
|
|
|
|
|
|
=encoding utf8 |
|
11
|
|
|
|
|
|
|
|
|
12
|
|
|
|
|
|
|
=head1 NAME |
|
13
|
|
|
|
|
|
|
|
|
14
|
|
|
|
|
|
|
Math::Symbolic::Custom::CollectSimplify - Simplify Math::Symbolic expressions using Math::Symbolic::Custom::Collect |
|
15
|
|
|
|
|
|
|
|
|
16
|
|
|
|
|
|
|
=head1 VERSION |
|
17
|
|
|
|
|
|
|
|
|
18
|
|
|
|
|
|
|
Version 0.2 |
|
19
|
|
|
|
|
|
|
|
|
20
|
|
|
|
|
|
|
=cut |
|
21
|
|
|
|
|
|
|
|
|
22
|
|
|
|
|
|
|
our $VERSION = '0.2'; |
|
23
|
|
|
|
|
|
|
|
|
24
|
2
|
|
|
2
|
|
544
|
use Math::Symbolic qw(:all); |
|
|
2
|
|
|
|
|
111196
|
|
|
|
2
|
|
|
|
|
394
|
|
|
25
|
2
|
|
|
2
|
|
16
|
use base 'Math::Symbolic::Custom::Simplification'; |
|
|
2
|
|
|
|
|
4
|
|
|
|
2
|
|
|
|
|
1097
|
|
|
26
|
2
|
|
|
2
|
|
3057
|
use Math::Symbolic::Custom::Collect 0.21; |
|
|
2
|
|
|
|
|
25324
|
|
|
|
2
|
|
|
|
|
17
|
|
|
27
|
|
|
|
|
|
|
|
|
28
|
|
|
|
|
|
|
=head1 SYNOPSIS |
|
29
|
|
|
|
|
|
|
|
|
30
|
|
|
|
|
|
|
use strict; |
|
31
|
|
|
|
|
|
|
use Math::Symbolic qw(:all); |
|
32
|
|
|
|
|
|
|
use Math::Symbolic::Custom::CollectSimplify; |
|
33
|
|
|
|
|
|
|
|
|
34
|
|
|
|
|
|
|
# 1. We have some expressions |
|
35
|
|
|
|
|
|
|
my $f1 = parse_from_string('2*(x+3)'); |
|
36
|
|
|
|
|
|
|
my $f2 = parse_from_string('(6*x+2)*(4+x)'); |
|
37
|
|
|
|
|
|
|
my $f3 = parse_from_string('3*x+(2*(x+1))'); |
|
38
|
|
|
|
|
|
|
|
|
39
|
|
|
|
|
|
|
# 2. Manipulate them in some way to create a big expression |
|
40
|
|
|
|
|
|
|
my $f4 = $f1 + $f2 + $f3; |
|
41
|
|
|
|
|
|
|
|
|
42
|
|
|
|
|
|
|
# 3. We want to simplify this |
|
43
|
|
|
|
|
|
|
print "Expression: $f4\n"; |
|
44
|
|
|
|
|
|
|
# Expression: ((2 * (x + 3)) + (((6 * x) + 2) * (4 + x))) + ((3 * x) + (2 * (x + 1))) |
|
45
|
|
|
|
|
|
|
|
|
46
|
|
|
|
|
|
|
# 4. Try with the simplify() that comes with Math::Symbolic |
|
47
|
|
|
|
|
|
|
my $f4_s1 = $f4->simplify(); |
|
48
|
|
|
|
|
|
|
|
|
49
|
|
|
|
|
|
|
print "Original: $f4_s1\n"; |
|
50
|
|
|
|
|
|
|
# Original: (((2 * (3 + x)) + ((2 + (6 * x)) * (4 + x))) + (2 * (1 + x))) + (3 * x) |
|
51
|
|
|
|
|
|
|
|
|
52
|
|
|
|
|
|
|
if ( $f4->test_num_equiv($f4_s1) ) { |
|
53
|
|
|
|
|
|
|
print "\t- Is numerically equivalent with original expression\n"; |
|
54
|
|
|
|
|
|
|
} |
|
55
|
|
|
|
|
|
|
|
|
56
|
|
|
|
|
|
|
# 5. Try with the simplify() in this module instead |
|
57
|
|
|
|
|
|
|
# redefine "simplify()" using the register() method |
|
58
|
|
|
|
|
|
|
Math::Symbolic::Custom::CollectSimplify->register(); |
|
59
|
|
|
|
|
|
|
|
|
60
|
|
|
|
|
|
|
my $f4_s2 = $f4->simplify(); |
|
61
|
|
|
|
|
|
|
|
|
62
|
|
|
|
|
|
|
print "New: $f4_s2\n"; |
|
63
|
|
|
|
|
|
|
# New: (16 + (33 * x)) + (6 * (x ^ 2)) |
|
64
|
|
|
|
|
|
|
|
|
65
|
|
|
|
|
|
|
if ( $f4->test_num_equiv($f4_s2) ) { |
|
66
|
|
|
|
|
|
|
print "\t- Is numerically equivalent with original expression\n"; |
|
67
|
|
|
|
|
|
|
} |
|
68
|
|
|
|
|
|
|
|
|
69
|
|
|
|
|
|
|
=head1 DESCRIPTION |
|
70
|
|
|
|
|
|
|
|
|
71
|
|
|
|
|
|
|
Redefines L's "simplify()" method using the Math::Symbolic module extension class L. This new simplify() method uses |
|
72
|
|
|
|
|
|
|
"to_collected()" in L. |
|
73
|
|
|
|
|
|
|
|
|
74
|
|
|
|
|
|
|
Be aware that "to_collected()" doesn't always produce a simpler expression from the inputted expression, because it does not factorize expressions. Setting the package variable |
|
75
|
|
|
|
|
|
|
C<$Math::Symbolic::Custom::CollectSimplify::TEST_COMPLEXITY> to 1 will make the simplify() routine check to see if the resultant expression is any simpler (using a measure |
|
76
|
|
|
|
|
|
|
of expression complexity based on the number of constants, variables and operators) and if not it will return the expression passed to it. Use this if you want to make sure you |
|
77
|
|
|
|
|
|
|
are getting the simplest possible expression. This behaviour is off by default. |
|
78
|
|
|
|
|
|
|
|
|
79
|
|
|
|
|
|
|
=cut |
|
80
|
|
|
|
|
|
|
|
|
81
|
|
|
|
|
|
|
our $TEST_COMPLEXITY = 0; |
|
82
|
|
|
|
|
|
|
|
|
83
|
|
|
|
|
|
|
sub simplify { |
|
84
|
3
|
|
|
3
|
0
|
174013
|
my $f1 = shift; |
|
85
|
|
|
|
|
|
|
|
|
86
|
|
|
|
|
|
|
# use to_collected() to (potentially) simplify it |
|
87
|
3
|
|
|
|
|
59
|
my $f2 = $f1->to_collected(); |
|
88
|
|
|
|
|
|
|
|
|
89
|
3
|
50
|
|
|
|
30740
|
if ( !defined $f2 ) { |
|
90
|
0
|
|
|
|
|
0
|
return $f1; |
|
91
|
|
|
|
|
|
|
} |
|
92
|
|
|
|
|
|
|
|
|
93
|
|
|
|
|
|
|
# compare on complexity and pass through the input expression |
|
94
|
|
|
|
|
|
|
# if the collected one is no simpler. |
|
95
|
|
|
|
|
|
|
|
|
96
|
3
|
100
|
|
|
|
12
|
if ( $TEST_COMPLEXITY ) { |
|
97
|
|
|
|
|
|
|
|
|
98
|
1
|
|
|
|
|
5
|
my $f1_score = test_complexity($f1); |
|
99
|
1
|
|
|
|
|
3
|
my $f2_score = test_complexity($f2); |
|
100
|
|
|
|
|
|
|
|
|
101
|
1
|
50
|
|
|
|
9
|
return $f1_score > $f2_score ? $f2 : $f1; |
|
102
|
|
|
|
|
|
|
} |
|
103
|
|
|
|
|
|
|
else { |
|
104
|
2
|
|
|
|
|
22
|
return $f2; |
|
105
|
|
|
|
|
|
|
} |
|
106
|
|
|
|
|
|
|
} |
|
107
|
|
|
|
|
|
|
|
|
108
|
|
|
|
|
|
|
|
|
109
|
|
|
|
|
|
|
# Try to achieve a measure of "complexity" of a Math::Symbolic expression. |
|
110
|
|
|
|
|
|
|
# The greater the score, the higher the "complexity". |
|
111
|
|
|
|
|
|
|
sub test_complexity { |
|
112
|
10
|
|
|
10
|
0
|
598072
|
my ($tree) = @_; |
|
113
|
|
|
|
|
|
|
|
|
114
|
|
|
|
|
|
|
# Look at: |
|
115
|
|
|
|
|
|
|
# 1. the depth of the tree |
|
116
|
|
|
|
|
|
|
# 2. the number of constants |
|
117
|
|
|
|
|
|
|
# 3. the number of variable instances (e.g. x * x should count as 2 variables) |
|
118
|
|
|
|
|
|
|
# 4. the number of operations |
|
119
|
10
|
|
|
|
|
69
|
my %metrics = ( depth => 0, constants => 0, variables => 0, operations => 0 ); |
|
120
|
10
|
|
|
|
|
52
|
walk($tree, 0, \%metrics); |
|
121
|
|
|
|
|
|
|
|
|
122
|
10
|
|
|
|
|
32
|
my $score = 0; |
|
123
|
|
|
|
|
|
|
# it should be possible to weight these metrics; |
|
124
|
|
|
|
|
|
|
# for now all metrics are at weight 1. |
|
125
|
10
|
|
|
|
|
49
|
$score += $_ for values %metrics; |
|
126
|
|
|
|
|
|
|
|
|
127
|
10
|
|
|
|
|
40
|
return $score; |
|
128
|
|
|
|
|
|
|
} |
|
129
|
|
|
|
|
|
|
|
|
130
|
|
|
|
|
|
|
# helper routine to walk the Math::Symbolic expression tree and tot up the metrics. |
|
131
|
|
|
|
|
|
|
sub walk { |
|
132
|
68
|
|
|
68
|
0
|
214
|
my ($node, $depth, $hr) = @_; |
|
133
|
|
|
|
|
|
|
|
|
134
|
68
|
100
|
|
|
|
182
|
$hr->{depth} = $depth if $depth > $hr->{depth}; |
|
135
|
|
|
|
|
|
|
|
|
136
|
68
|
100
|
|
|
|
186
|
if ($node->term_type() == T_CONSTANT) { |
|
|
|
100
|
|
|
|
|
|
|
137
|
22
|
|
|
|
|
102
|
$hr->{constants}++; |
|
138
|
|
|
|
|
|
|
} elsif ($node->term_type() == T_VARIABLE) { |
|
139
|
17
|
|
|
|
|
113
|
$hr->{variables}++; |
|
140
|
|
|
|
|
|
|
} else { |
|
141
|
29
|
|
|
|
|
172
|
$hr->{operations}++; |
|
142
|
29
|
|
|
|
|
43
|
foreach my $child (@{$node->{operands}}) { |
|
|
29
|
|
|
|
|
129
|
|
|
143
|
58
|
|
|
|
|
121
|
walk($child, $depth + 1, $hr); |
|
144
|
|
|
|
|
|
|
} |
|
145
|
|
|
|
|
|
|
} |
|
146
|
|
|
|
|
|
|
} |
|
147
|
|
|
|
|
|
|
|
|
148
|
|
|
|
|
|
|
=head1 SEE ALSO |
|
149
|
|
|
|
|
|
|
|
|
150
|
|
|
|
|
|
|
L |
|
151
|
|
|
|
|
|
|
|
|
152
|
|
|
|
|
|
|
L |
|
153
|
|
|
|
|
|
|
|
|
154
|
|
|
|
|
|
|
L |
|
155
|
|
|
|
|
|
|
|
|
156
|
|
|
|
|
|
|
=head1 AUTHOR |
|
157
|
|
|
|
|
|
|
|
|
158
|
|
|
|
|
|
|
Matt Johnson, C<< >> |
|
159
|
|
|
|
|
|
|
|
|
160
|
|
|
|
|
|
|
=head1 ACKNOWLEDGEMENTS |
|
161
|
|
|
|
|
|
|
|
|
162
|
|
|
|
|
|
|
Steffen Mueller, author of Math::Symbolic |
|
163
|
|
|
|
|
|
|
|
|
164
|
|
|
|
|
|
|
=head1 LICENSE AND COPYRIGHT |
|
165
|
|
|
|
|
|
|
|
|
166
|
|
|
|
|
|
|
This software is copyright (c) 2024 by Matt Johnson. |
|
167
|
|
|
|
|
|
|
|
|
168
|
|
|
|
|
|
|
This is free software; you can redistribute it and/or modify it under |
|
169
|
|
|
|
|
|
|
the same terms as the Perl 5 programming language system itself. |
|
170
|
|
|
|
|
|
|
|
|
171
|
|
|
|
|
|
|
=cut |
|
172
|
|
|
|
|
|
|
|
|
173
|
|
|
|
|
|
|
1; |
|
174
|
|
|
|
|
|
|
__END__ |