File Coverage

blib/lib/Math/Vector/Real/MultiNormalMixture.pm
Criterion Covered Total %
statement 15 71 21.1
branch 0 10 0.0
condition 0 8 0.0
subroutine 5 10 50.0
pod 5 5 100.0
total 25 104 24.0


line stmt bran cond sub pod time code
1             package Math::Vector::Real::MultiNormalMixture;
2              
3             our $VERSION = '0.02';
4              
5 1     1   24460 use 5.010;
  1         4  
  1         46  
6 1     1   8 use strict;
  1         2  
  1         53  
7 1     1   5 use warnings;
  1         7  
  1         39  
8 1     1   5 use Carp;
  1         2  
  1         108  
9              
10 1     1   1183 use Math::Vector::Real;
  1         23023  
  1         1691  
11              
12             my $PI = 3.14159265358979323846264338327950288419716939937510;
13              
14             sub new {
15 0     0 1   my ($class, %opts) = @_;
16 0   0       my $mu = delete $opts{mu} // croak "required argument 'mu' missing";
17 0 0         @$mu >= 1 or die "'mu' must containt one point at least";
18 0           my $dim = @{$mu->[0]};
  0            
19 0           my $n = @$mu;
20 0           my $alpha = delete $opts{alpha};
21 0 0         my @alpha = (defined $alpha ? @$alpha : ((1/$n) x $n));
22 0   0       my $sigma = delete $opts{sigma} // 1.0;
23 0 0         my @sigma = (ref $sigma ? @$sigma : (($sigma) x $n));
24 0 0 0       croak "bad array size" unless (@alpha == $n and @sigma == $n);
25 0           my $c1 = (2 * $PI) ** (-0.5 * $dim);
26 0           my @c = map $alpha[$_] * $c1 * ($sigma[$_] ** (-$dim)), (0..$#alpha);
27 0           my @isigma2 = map 1.0/($_ * $_), @sigma;
28 0           my $c2 = -sqrt(2.0 / $PI);
29 0           my @second = map $c2/($_ * $_ * $_), @sigma;
30              
31 0           my $self = { mu => [map Math::Vector::Real::clone($_), @$mu],
32             alpha => \@alpha,
33             sigma => \@sigma,
34             isigma2 => \@isigma2,
35             c => \@c,
36             second => \@second,
37             dim => $dim,
38             };
39 0           bless $self, $class;
40             }
41              
42             sub density {
43 0     0 1   my ($self, $p) = @_;
44 0           my $c = $self->{c};
45 0           my $mu = $self->{mu};
46 0           my $isigma2 = $self->{sigma};
47 0           my $acu = 0;
48 0           for (0..$#$mu) {
49 0           $acu += $c->[$_] * exp(-$isigma2->[$_] * $mu->[$_]->dist2($p));
50             }
51 0           $acu;
52             }
53              
54             sub density_portion {
55 0     0 1   my $self = shift;
56 0           my $p = shift;
57 0           my $c = $self->{c};
58 0           my $mu = $self->{mu};
59 0           my $isigma2 = $self->{sigma};
60 0           my $acu = 0;
61 0           for (@_) {
62 0           $acu += $c->[$_] * exp(-$isigma2->[$_] * $mu->[$_]->dist2($p));
63             }
64 0           $acu;
65             }
66              
67             sub density_and_gradient {
68 0     0 1   my ($self, $p) = @_;
69 0           my $c = $self->{c};
70 0           my $mu = $self->{mu};
71 0           my $isigma2 = $self->{sigma};
72 0           my $d = 0;
73 0           my $g = $Math::Vector::Real->zero(scalar @{$mu->[0]});
  0            
74 0           for (0..$#$mu) {
75 0           my $mu_p = $p - $mu->[$_];
76 0           my $isigma2 = $isigma2->[$_];
77 0           my $Dd = $c->[$_] * exp(-$isigma2 * $mu_p->abs2);
78 0           $d += $Dd;
79 0           $g += -2 * $isigma2 * $mu_p * $Dd;
80             }
81 0           return ($d, $g);
82             }
83              
84             sub max_density_estimation {
85 0     0 1   my $self = shift;
86 0           my $mu = $self->{mu};
87 0           my $max = $self->density($mu->[0]);
88 0           for my $ix (1..$#$mu) {
89 0           my $d = $self->density($mu->[$ix]);
90             # print "d: $d\n";
91 0 0         $max = $d if $d > $max;
92             }
93 0           return $max;
94             }
95              
96             1;
97             __END__