File Coverage

blib/lib/ICC/Support/geo1.pm
Criterion Covered Total %
statement 14 104 13.4
branch 1 46 2.1
condition 1 24 4.1
subroutine 4 11 36.3
pod 1 6 16.6
total 21 191 10.9


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