File Coverage

lib/ICC/Support/geo1.pm
Criterion Covered Total %
statement 17 107 15.8
branch 1 46 2.1
condition 1 24 4.1
subroutine 5 12 41.6
pod 1 6 16.6
total 25 195 12.8


line stmt bran cond sub pod time code
1             package ICC::Support::geo1;
2              
3 2     2   98105 use strict;
  2         12  
  2         90  
4 2     2   12 use Carp;
  2         4  
  2         137  
5              
6             our $VERSION = 0.11;
7              
8             # revised 2016-05-17
9             #
10             # Copyright © 2004-2019 by William B. Birkett
11              
12             # add development directory
13 2     2   469 use lib 'lib';
  2         662  
  2         14  
14              
15             # inherit from Shared
16 2     2   676 use parent qw(ICC::Shared);
  2         298  
  2         12  
17              
18             # create new object
19             # hash keys are:
20             # 'points' value is an array reference containing center coordinates
21             # 'matrix' value is an optional weight matrix, sometimes the inverse covariance matrix
22             # the weight matrix must have the same dimension as the center coordinate
23             # parameters: ([ref_to_parameter_hash])
24             # returns: (ref_to_object)
25             sub new {
26              
27             # get object class
28 1     1 0 794 my $class = shift();
29              
30             # create empty geo1 object
31 1         4 my $self = [
32             {}, # object header
33             [], # points array
34             [] # weight matrix
35             ];
36              
37             # if one parameter, a hash reference
38 1 50 33     6 if (@_ == 1 && ref($_[0]) eq 'HASH') {
39            
40             # create new object from parameter hash
41 0         0 _new_from_hash($self, @_);
42            
43             }
44              
45             # bless object
46 1         2 bless($self, $class);
47              
48             # return object reference
49 1         3 return($self);
50              
51             }
52              
53             # compute radius
54             # parameters: (ref_to_input_array)
55             # returns: (radius)
56             sub transform {
57              
58             # get parameters
59 0     0 0   my ($self, $in) = @_;
60              
61             # if object has covariance matrix
62 0 0 0       if (defined($self->[2]) && (0 != @{$self->[2]})) {
  0            
63            
64             # return Mahalanobis distance
65 0           return(ICC::Support::Lapack::mahal($in, $self->[1], $self->[2]));
66            
67             } else {
68            
69             # return Euclidean distance
70 0           return(ICC::Support::Lapack::euclid($in, $self->[1]));
71            
72             }
73            
74             }
75              
76             # compute Jacobian matrix
77             # parameters: (ref_to_input_array)
78             # returns: (Jacobian_matrix, [radius])
79             sub jacobian {
80              
81             # get parameters
82 0     0 0   my ($self, $in) = @_;
83              
84             # compute radial Jacobian
85 0           my ($jac, $r) = _radjac($self, $in);
86              
87             # if array wanted
88 0 0         if (wantarray) {
89            
90             # return Jacobian matrix and radius
91 0           return(Math::Matrix->new($jac), $r);
92            
93             } else {
94            
95             # return Jacobian matrix
96 0           return(Math::Matrix->new($jac));
97            
98             }
99            
100             }
101              
102             # get/set points array
103             # parameters: ([ref_to_points_array])
104             # returns: (ref_to_points_array)
105             sub points {
106              
107             # get parameters
108 0     0 0   my ($self, $points) = @_;
109              
110             # if parameter supplied
111 0 0         if (defined($points)) {
112            
113             # verify an array reference
114 0 0         (ref($points) eq 'ARRAY') or croak('\'points\' parameter not an array');
115            
116             # verify point coordinates
117 0 0         (@{$points} == grep {Scalar::Util::looks_like_number($_)} @{$points}) or croak('\'points\' array contains invalid coordinates');
  0            
  0            
  0            
118            
119             # copy points array
120 0           $self->[1] = [@{$points}];
  0            
121            
122             }
123              
124             # return end point array reference
125 0           return($self->[1]);
126              
127             }
128              
129             # get/set weight matrix
130             # parameters: ([ref_to_weight_matrix])
131             # returns: (ref_to_weight_matrix)
132             sub matrix {
133              
134             # get parameters
135 0     0 0   my ($self, $matrix) = @_;
136              
137             # if parameter supplied
138 0 0         if (defined($matrix)) {
139            
140             # verify a 2-D array or Math::Matrix object
141 0 0 0       ((ref($matrix) eq 'ARRAY' && @{$matrix} == grep {ref() eq 'ARRAY'} @{$matrix}) || UNIVERSAL::isa($matrix, 'Math::Matrix')) or croak('\'matrix\' parameter not a 2-D array or Math::Matrix object');
  0   0        
  0            
  0            
142            
143             # verify first row contains only numeric values
144 0 0         (@{$matrix->[0]} == grep {Scalar::Util::looks_like_number($_)} @{$matrix->[0]}) or croak('\'matrix\' parameter contains non-numeric values');
  0            
  0            
  0            
145            
146             # verify matrix is square
147 0 0         (@{$matrix} == @{$matrix->[0]}) or croak('\'matrix\' parameter not square matrix');
  0            
  0            
148            
149             # copy weight matrix
150 0           $self->[2] = Storable::dclone($matrix);
151            
152             }
153              
154             # return matrix reference
155 0           return($self->[2]);
156              
157             }
158              
159             # print object contents to string
160             # format is an array structure
161             # parameter: ([format])
162             # returns: (string)
163             sub sdump {
164              
165             # get parameters
166 0     0 1   my ($self, $p) = @_;
167              
168             # local variables
169 0           my ($s, $fmt);
170              
171             # resolve parameter to an array reference
172 0 0         $p = defined($p) ? ref($p) eq 'ARRAY' ? $p : [$p] : [];
    0          
173              
174             # get format string
175 0 0 0       $fmt = defined($p->[0]) && ! ref($p->[0]) ? $p->[0] : 'undef';
176              
177             # set string to object ID
178 0           $s = sprintf("'%s' object, (0x%x)\n", ref($self), $self);
179              
180             # return
181 0           return($s);
182              
183             }
184              
185             # compute radial Jacobian
186             # parameters: (ref_to_object, ref_to_input_vector)
187             # returns: (Jacobian_vector, radius)
188             sub _radjac {
189              
190             # get parameters
191 0     0     my ($self, $in) = @_;
192              
193             # local variables
194 0           my ($jac, $r, $wwt);
195              
196             # for each dimension
197 0           for my $i (0 .. $#{$self->[1]}) {
  0            
198            
199             # compute input vector element
200 0           $jac->[$i] = ($in->[$i] - $self->[1][$i]);
201            
202             }
203              
204             # if object has weight matrix
205 0 0 0       if (defined($self->[2]) && @{$self->[2]}) {
  0            
206            
207             # radius is Mahalanobis distance
208 0           $r = ICC::Support::Lapack::mahal($in, $self->[1], $self->[2]);
209            
210             # for each row
211 0           for my $i (0 .. $#{$self->[2]}) {
  0            
212            
213             # for each column
214 0           for my $j (0 .. $#{$self->[2][0]}) {
  0            
215            
216             # set matrix element (W + Wᵀ)/2
217 0           $wwt->[$i][$j] = ($self->[2][$i][$j] + $self->[2][$j][$i])/2;
218            
219             }
220            
221             }
222            
223             # multiply Jacobian by (W + Wᵀ)/2 matrix
224 0           $jac = ICC::Support::Lapack::vec_xplus($wwt, $jac, {'trans' => 'T'});
225            
226             } else {
227            
228             # radius is Euclidean distance
229 0           $r = ICC::Support::Lapack::euclid($in, $self->[1]);
230            
231             }
232              
233             # if radius is zero
234 0 0         if ($r == 0) {
235            
236             # set Jacobian to all ones
237 0           $jac = [(1) x @{$self->[1]}];
  0            
238            
239             } else {
240            
241             # for each dimension
242 0           for my $i (0 .. $#{$self->[1]}) {
  0            
243            
244             # divide by radius
245 0           $jac->[$i] /= $r;
246            
247             }
248            
249             }
250              
251             # return
252 0           return($jac, $r);
253              
254             }
255              
256             # populate object from parameter hash
257             # parameters: (object_reference, ref_to_parameter_hash)
258             sub _new_from_hash {
259              
260             # get parameters
261 0     0     my ($self, $hash) = @_;
262              
263             # local variables
264 0           my ($points, $matrix, $info);
265              
266             # get points array
267 0 0         if ($points = $hash->{'points'}) {
268            
269             # verify an array reference
270 0 0         (ref($points) eq 'ARRAY') or croak('\'points\' parameter not an array');
271            
272             # verify point coordinates
273 0 0         (@{$points} == grep {Scalar::Util::looks_like_number($_)} @{$points}) or croak('\'points\' array contains invalid coordinates');
  0            
  0            
  0            
274            
275             # copy points array
276 0           $self->[1] = [@{$points}];
  0            
277            
278             }
279              
280             # get weight matrix
281 0 0         if ($matrix = $hash->{'matrix'}) {
282            
283             # verify a 2-D array or Math::Matrix object
284 0 0 0       ((ref($matrix) eq 'ARRAY' && @{$matrix} == grep {ref() eq 'ARRAY'} @{$matrix}) || UNIVERSAL::isa($matrix, 'Math::Matrix')) or croak('\'matrix\' parameter not a 2-D array or Math::Matrix object');
  0   0        
  0            
  0            
285            
286             # verify first row contains only numeric values
287 0 0         (@{$matrix->[0]} == grep {Scalar::Util::looks_like_number($_)} @{$matrix->[0]}) or croak('\'matrix\' parameter contains non-numeric values');
  0            
  0            
  0            
288            
289             # verify matrix is square
290 0 0         (@{$matrix} == @{$matrix->[0]}) or croak('\'matrix\' parameter not square matrix');
  0            
  0            
291            
292             # verify matrix and center have same dimensions
293 0 0         (@{$matrix} == @{$self->[1]}) or croak('\'matrix\' and \'center\' have different dimensions');
  0            
  0            
294            
295             # copy weight matrix
296 0           $self->[2] = Storable::dclone($matrix);
297            
298             }
299              
300             }
301              
302             1;