File Coverage

blib/lib/Math/Approx.pm
Criterion Covered Total %
statement 12 84 14.2
branch 0 6 0.0
condition 0 2 0.0
subroutine 4 10 40.0
pod 5 5 100.0
total 21 107 19.6


line stmt bran cond sub pod time code
1             # -*- Mode: Perl -*-
2             # $Basename: Approx.pm $
3             # $Revision: 1.2 $
4             # Author : Ulrich Pfeifer
5             # Created On : Wed Oct 25 10:50:38 1995
6             # Last Modified By: Ulrich Pfeifer
7             # Last Modified On: Tue Mar 3 15:14:30 1998
8             # Language : Perl
9             #
10             # (C) Copyright 1995,1998, Ulrich Pfeifer
11             #
12              
13             =head1 NAME
14              
15             Math::Approx
16              
17             =head1 METHODS
18              
19             =head2 new (constructor)
20              
21             Math::Approx->new(\&poly, $degree, %data);
22              
23             If the first argument to the constructor is a CODE reference, this is
24             used as the function to iterate over the data. Such a function must
25             take two arguments: The I and the I value.
26              
27             For interpolation with plain polynomials I can be defined as:
28              
29             sub poly {
30             my($n,$x) = @_;
31             return $x ** $n;
32             }
33              
34             If the first argument in the constructor is a FALSE value instead of a
35             CODE reference, then the above plain polynomial I is used as the
36             iterator function.
37              
38             The second argument is the maximum degree which should be used for
39             interpolation. Degrees start with B<0>.
40              
41             The rest of the arguments are treated as pairs of B and B
42             samples which should be approximated.
43              
44             The constructor returns a Math::Approx reference.
45              
46             =head2 approx
47              
48             $approximation->approx(17);
49              
50             The method returns the approximated B value for the B value
51             given as argument.
52              
53             =head2 fit
54              
55             $approximation->fit;
56              
57             Returns the medim square error for the data points.
58              
59             =head2 plot
60              
61             $approximation->plot("tmp/app");
62              
63             Prints all data pairs and the corresponding approximation pairs into
64             the filename given as argument. The output is suitable for usage with
65             gnuplot(1).
66              
67              
68             =head2 print
69              
70             $approximation->print;
71              
72             Prints information about the approximation on I
73              
74             =head1 EXAMPLE
75              
76             use Math::Approx;
77            
78             sub poly {
79             my($n,$x) = @_;
80             return $x ** $n;
81             }
82            
83             for (1..20) {
84             $x{$_} = sin($_/10)*cos($_/30)+0.3*rand;
85             }
86            
87             $a = new Math::Approx (\&poly, 5, %x);
88             $a->print;
89             $a->plot("math-approx-demo.out");
90             print "Fit: ", $a->fit, "\n";
91              
92             =head1 SEE ALSO
93              
94             gnuplot(1).
95              
96             =head1 AUTHOR
97              
98             Ulrich Pfeifer EFE
99              
100             =cut
101              
102              
103             package Math::Approx;
104 1     1   15557 use Math::Matrix;
  1         6827  
  1         37  
105 1     1   39 use 5.004;
  1         4  
  1         33  
106 1     1   5 use strict;
  1         7  
  1         49  
107 1     1   5 use vars qw($VERSION);
  1         2  
  1         1073  
108              
109             # $Format: "$VERSION = sprintf '%5.3f', $ProjectVersion$;"$
110             $VERSION = sprintf '%5.3f', 0.2;
111              
112             sub new {
113 0     0 1   my $type = shift;
114 0   0 0     my $func = shift || sub {my($n,$x)=@_; $x**$n;};
  0            
  0            
115 0           my $degr = shift;
116 0 0         die "Math::Approx->new : invalid degree [$degr]" unless $degr;
117              
118 0           my %data = @_;
119 0 0         die "Math::Approx->new : empty data set" unless %data;
120              
121 0           my $self = {};
122 0           my @m;
123             my @x;
124              
125 0           $self->{'F'} = $func; # function of two arguments
126 0           $self->{'N'} = $degr;
127 0           $self->{'D'} = \%data;
128              
129 0           for my $x (keys %data) {
130 0           my $row = [];
131 0           for my $n (0 .. $degr) {
132 0           push @{$row}, &{$func}($n, $x);
  0            
  0            
133             }
134 0           push @x, $data{$x};
135 0           push(@m, $row);
136             }
137 0           my $I = new Math::Matrix(@m); # $I->print("Initial\n");
138 0           my $T = $I->transpose->multiply($I); # $T->print("Quadratic\n");
139 0           my $x = new Math::Matrix(\@x); # $x->print("X\n");
140 0           my $tx = $I->transpose->
141             multiply($x->transpose); # $tx->print("TX\n");
142 0           my $eq = $T->concat($tx); # $eq->print("EQN\n");
143 0           my $s = $eq->solve; # $s->print("SOLUTION\n");
144             # $T->multiply($s)->print("TEST\n");
145 0           $self->{'A'} = $s->transpose->[0];
146 0           bless $self, $type;
147             }
148              
149             sub approx {
150 0     0 1   my $self = shift;
151 0           my $x = shift;
152 0           my $func = $self->{'F'};
153 0           my $degr = $self->{'N'};
154 0           my $result;
155              
156 0           for my $n (0 .. $degr) {
157 0           $result += &{$func}($n, $x) * $self->{'A'}->[$n];
  0            
158             }
159              
160 0           $result;
161             }
162              
163             sub fit {
164 0     0 1   my $self = shift;
165 0           my $result;
166             my $n;
167              
168 0           for my $key (keys %{$self->{'D'}}) {
  0            
169 0           $result += ($self->{'D'}->{$key}-$self->approx($key))**2;
170             #print STDERR "## $result\n";
171 0           $n++;
172             }
173              
174 0           $result/$n;
175             }
176              
177             sub print {
178 0     0 1   my $self = shift;
179            
180 0           printf "Function: %s\n", $self->{'F'};
181 0           printf "Degree: %d\n", $self->{'N'};
182 0           print "Koeff: ", join(' ', @{$self->{'A'}}), "\n";
  0            
183 0           print "Fit: ", $self->fit, "\n";
184 0           print "Data:\n";
185 0           print " X Y Approximation\n";
186 0           for my $key (sort {$a <=> $b} keys %{$self->{'D'}}) {
  0            
  0            
187 0           printf("%10.5f %10.5f %10.5f\n", $key, $self->{'D'}->{$key},
188             $self->approx($key));
189             }
190             }
191              
192             sub plot {
193 0     0 1   my $self = shift;
194 0           my $file = shift;
195 0 0         open(OUT, ">$file") || die "Could not open $file: $!\n";
196            
197 0           print OUT "\n#data\n";
198 0           for my $key (sort {$a <=> $b} keys %{$self->{'D'}}) {
  0            
  0            
199 0           print OUT $key, ' ', $self->{'D'}->{$key}, "\n";
200             }
201              
202 0           print OUT "\n#Approximation\n";
203 0           for my $key (sort {$a <=> $b} keys %{$self->{'D'}}) {
  0            
  0            
204 0           print OUT $key, ' ', $self->approx($key), "\n";
205             }
206 0           close(OUT);
207 0           1;
208             }
209              
210             1;