line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
package Math::GSL::Linalg::SVD; |
2
|
|
|
|
|
|
|
|
3
|
1
|
|
|
1
|
|
23743
|
use 5.010000; |
|
1
|
|
|
|
|
3
|
|
|
1
|
|
|
|
|
34
|
|
4
|
1
|
|
|
1
|
|
5
|
use strict; |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
30
|
|
5
|
1
|
|
|
1
|
|
4
|
use warnings; |
|
1
|
|
|
|
|
5
|
|
|
1
|
|
|
|
|
48
|
|
6
|
1
|
|
|
1
|
|
5
|
use Carp; |
|
1
|
|
|
|
|
1
|
|
|
1
|
|
|
|
|
121
|
|
7
|
|
|
|
|
|
|
|
8
|
|
|
|
|
|
|
#=fs Config |
9
|
|
|
|
|
|
|
|
10
|
|
|
|
|
|
|
require Exporter; |
11
|
1
|
|
|
1
|
|
819
|
use AutoLoader qw(AUTOLOAD); |
|
1
|
|
|
|
|
1623
|
|
|
1
|
|
|
|
|
7
|
|
12
|
|
|
|
|
|
|
|
13
|
|
|
|
|
|
|
our @ISA = qw(Exporter); |
14
|
|
|
|
|
|
|
our %EXPORT_TAGS = ( 'all' => [ qw( |
15
|
|
|
|
|
|
|
|
16
|
|
|
|
|
|
|
) ] ); |
17
|
|
|
|
|
|
|
|
18
|
|
|
|
|
|
|
our @EXPORT_OK = ( @{ $EXPORT_TAGS{'all'} } ); |
19
|
|
|
|
|
|
|
|
20
|
|
|
|
|
|
|
our @EXPORT = qw( |
21
|
|
|
|
|
|
|
|
22
|
|
|
|
|
|
|
); |
23
|
|
|
|
|
|
|
|
24
|
|
|
|
|
|
|
#=fe |
25
|
|
|
|
|
|
|
|
26
|
|
|
|
|
|
|
our $VERSION = '0.0.2'; |
27
|
|
|
|
|
|
|
|
28
|
|
|
|
|
|
|
#=fs Config |
29
|
|
|
|
|
|
|
|
30
|
|
|
|
|
|
|
require XSLoader; |
31
|
|
|
|
|
|
|
XSLoader::load('Math::GSL::Linalg::SVD', $VERSION); |
32
|
|
|
|
|
|
|
|
33
|
|
|
|
|
|
|
#=fe |
34
|
|
|
|
|
|
|
|
35
|
|
|
|
|
|
|
|
36
|
|
|
|
|
|
|
#////////////////////////////////////////////// Convinience Methods /////////////////////////////////////////////////// |
37
|
|
|
|
|
|
|
|
38
|
|
|
|
|
|
|
|
39
|
|
|
|
|
|
|
=head1 NAME |
40
|
|
|
|
|
|
|
|
41
|
|
|
|
|
|
|
Math::GSL::Linalg::SVD - Perl extension with convenience methods for performing SVD and eigenvector decomp with the gsl C libraries. |
42
|
|
|
|
|
|
|
|
43
|
|
|
|
|
|
|
=cut |
44
|
|
|
|
|
|
|
|
45
|
|
|
|
|
|
|
=head1 SYNOPSIS |
46
|
|
|
|
|
|
|
|
47
|
|
|
|
|
|
|
use Math::GSL::Linalg::SVD; |
48
|
|
|
|
|
|
|
|
49
|
|
|
|
|
|
|
# Create object. |
50
|
|
|
|
|
|
|
my $svd = Math::GSL::Linalg::SVD->new( { verbose => 1 } ); |
51
|
|
|
|
|
|
|
|
52
|
|
|
|
|
|
|
my $data = [ |
53
|
|
|
|
|
|
|
[qw/ 9.515970281313E-01 1.230695618728E-01 -1.652767938310E-01 /], |
54
|
|
|
|
|
|
|
[qw/ -1.788010086499E-01 3.654739881179E-01 8.526964090247E-02 /], |
55
|
|
|
|
|
|
|
[qw/ 4.156708817272E-02 5.298288357316E-02 7.130047145031E-01 /], |
56
|
|
|
|
|
|
|
]; |
57
|
|
|
|
|
|
|
|
58
|
|
|
|
|
|
|
# Load data. |
59
|
|
|
|
|
|
|
$svd->load_data( { data => $data } ); |
60
|
|
|
|
|
|
|
|
61
|
|
|
|
|
|
|
# Perform singular value decomposition using the Golub-Reinsch algorithm (this is the default - see METHODS). |
62
|
|
|
|
|
|
|
# To perform eigen decomposition pass 'eign' as algorithm argument - see METHODS. |
63
|
|
|
|
|
|
|
$svd->decompose( { algorithm => q{gd} } ); |
64
|
|
|
|
|
|
|
|
65
|
|
|
|
|
|
|
# Pass results - see METHODS for more details. |
66
|
|
|
|
|
|
|
my ($S_vec_ref, $U_mat_ref, $V_mat_ref, $original_data_ref) = $svd->results; |
67
|
|
|
|
|
|
|
|
68
|
|
|
|
|
|
|
# Print elements of vector S. |
69
|
|
|
|
|
|
|
print qq{\nPrint diagonal elements in vector S\n}; |
70
|
|
|
|
|
|
|
for my $s (@{$S_vec_ref}) { print qq{$s, }; } |
71
|
|
|
|
|
|
|
|
72
|
|
|
|
|
|
|
# Print elements of matrix U. |
73
|
|
|
|
|
|
|
print qq{\nPrint matrix U\n}; |
74
|
|
|
|
|
|
|
for my $r (0..$#{$U_mat_ref}) { |
75
|
|
|
|
|
|
|
for my $c (0..$#{$U_mat_ref->[$r]}) { print qq{$U_mat_ref->[$r][$c], } }; print qq{\n}; } |
76
|
|
|
|
|
|
|
|
77
|
|
|
|
|
|
|
# Print elements of matrix V. |
78
|
|
|
|
|
|
|
print qq{\nPrint matrix V\n}; |
79
|
|
|
|
|
|
|
for my $r (0..$#{$V_mat_ref}) { |
80
|
|
|
|
|
|
|
for my $c (0..$#{$V_mat_ref->[$r]}) { print qq{$V_mat_ref->[$r][$c], } }; print qq{\n}; } |
81
|
|
|
|
|
|
|
|
82
|
|
|
|
|
|
|
=cut |
83
|
|
|
|
|
|
|
|
84
|
|
|
|
|
|
|
=head1 DESCRIPTION |
85
|
|
|
|
|
|
|
|
86
|
|
|
|
|
|
|
The singular value decomposition (SVD) is an important factorization of a rectangular real matrix - see |
87
|
|
|
|
|
|
|
http://en.wikipedia.org/wiki/Singular_value_decomposition. Eigendecomposition is the factorization of a |
88
|
|
|
|
|
|
|
matrix into a canonical form, whereby the matrix is represented in terms of its eigenvalues and |
89
|
|
|
|
|
|
|
eigenvectors - see http://en.wikipedia.org/wiki/Eigendecomposition_of_a_matrix. This module implements the |
90
|
|
|
|
|
|
|
SVD and Eigen decomposition routines of the The C GNU Scientific Library (GSL). It provides simple convinience methods in the |
91
|
|
|
|
|
|
|
upper-level Math::GSL::Linalg::SVD namespace to perform these operations. Alternatively, it also provides direct access |
92
|
|
|
|
|
|
|
to the C routines in the Math::GSL::Linalg::SVD::Matrix, Math::GSL::Linalg::SVD::Vector and |
93
|
|
|
|
|
|
|
Math::GSL::Linalg::SVD::Eigen namespaces - see METHODS. |
94
|
|
|
|
|
|
|
|
95
|
|
|
|
|
|
|
=cut |
96
|
|
|
|
|
|
|
|
97
|
|
|
|
|
|
|
=head1 METHODS |
98
|
|
|
|
|
|
|
|
99
|
|
|
|
|
|
|
This is a C-Wrapper for the gsl SVD and Eigen decomp routines. Its provides two means of accessing them. First, a basic |
100
|
|
|
|
|
|
|
OO-interface of convenience methods to allow simple use of the various sdv and eigen routines within the Math::GSL::Linalg::SVD namespaces. Second, |
101
|
|
|
|
|
|
|
it allows you to use the various routines directly using an object interface for the various C structure types. These |
102
|
|
|
|
|
|
|
exist within specific lower-level namespaces for convenience - see below. |
103
|
|
|
|
|
|
|
|
104
|
|
|
|
|
|
|
=head2 Math::GSL::Linalg::SVD |
105
|
|
|
|
|
|
|
|
106
|
|
|
|
|
|
|
=head3 new |
107
|
|
|
|
|
|
|
|
108
|
|
|
|
|
|
|
Create a new Math:GSL::Linalg::SVD object. |
109
|
|
|
|
|
|
|
|
110
|
|
|
|
|
|
|
my $svd = Math::GSL::Linalg::SVD->new(); |
111
|
|
|
|
|
|
|
# Pass verbose to turn on minimal messages. |
112
|
|
|
|
|
|
|
my $svd = Math::GSL::Linalg::SVD->new( { verbose => 1 } ); |
113
|
|
|
|
|
|
|
|
114
|
|
|
|
|
|
|
=head3 load_data |
115
|
|
|
|
|
|
|
|
116
|
|
|
|
|
|
|
Used for loading data into object. Data is fed as a reference to a LoL within an anonymous hash using the named argument |
117
|
|
|
|
|
|
|
'data'. |
118
|
|
|
|
|
|
|
|
119
|
|
|
|
|
|
|
$svd->load_data( { data => [ |
120
|
|
|
|
|
|
|
[qw/ 9.515970281313E-01 1.230695618728E-01 /], |
121
|
|
|
|
|
|
|
[qw/ -1.788010086499E-01 3.654739881179E-01 /], |
122
|
|
|
|
|
|
|
[qw/ 4.156708817272E-02 5.298288357316E-02 /], |
123
|
|
|
|
|
|
|
] } ); |
124
|
|
|
|
|
|
|
|
125
|
|
|
|
|
|
|
=head3 decompose |
126
|
|
|
|
|
|
|
|
127
|
|
|
|
|
|
|
Performs one of several different singular value decomposition algorithms on the loaded matrix (or computes eigenvalues |
128
|
|
|
|
|
|
|
and eigenvectors) depending on argument passed with with 'algorithm' argument. To use the Golub-Reinsch algorithm |
129
|
|
|
|
|
|
|
implemented by C pass 'gd'. To use the modified Golub-Reinsch algorithm implemented by |
130
|
|
|
|
|
|
|
C pass 'mod'. To use the one-sided Jacobi orthogonalization algorithm |
131
|
|
|
|
|
|
|
implemented by C pass 'jacobi'. To perform the eigenvalue and eigenvector calculations |
132
|
|
|
|
|
|
|
implemented by C pass 'eigen'. See |
133
|
|
|
|
|
|
|
http://www.gnu.org/software/gsl/manual/html_node/Singular-Value-Decomposition.html for further details. |
134
|
|
|
|
|
|
|
|
135
|
|
|
|
|
|
|
# Perform svd using the Golub-Reinsch algorithm pass 'gd' or nothing. |
136
|
|
|
|
|
|
|
$svd->decompose(); |
137
|
|
|
|
|
|
|
$svd->decompose( { algorithm => q{mod} } ); |
138
|
|
|
|
|
|
|
$svd->decompose( { algorithm => q{jacobi} } ); |
139
|
|
|
|
|
|
|
$eigen->decompose( { algorithm => q{eigen} } ); |
140
|
|
|
|
|
|
|
|
141
|
|
|
|
|
|
|
|
142
|
|
|
|
|
|
|
=head3 results |
143
|
|
|
|
|
|
|
|
144
|
|
|
|
|
|
|
Used to access the results of the analysis. Called in LIST context. For svd an ordered LIST of the LoLs is returned |
145
|
|
|
|
|
|
|
(corresponding to Vector S, Matrix U, Matrix V and Matrix A (see |
146
|
|
|
|
|
|
|
http://www.gnu.org/software/gsl/manual/html_node/Singular-Value-Decomposition.html). See SYNOPSIS. |
147
|
|
|
|
|
|
|
|
148
|
|
|
|
|
|
|
my ($S_vec_ref, $U_mat_ref, $V_mat_ref, $original_data_ref) = $svd->results; |
149
|
|
|
|
|
|
|
|
150
|
|
|
|
|
|
|
For eigen computation an ordered list of LoLs is returned corresponding to unordered eigenvalues, the eigenvectors (in |
151
|
|
|
|
|
|
|
the same order as the eigenvalues) and the original data matrix. See |
152
|
|
|
|
|
|
|
http://www.gnu.org/software/gsl/manual/html_node/Real-Symmetric-Matrices.html. |
153
|
|
|
|
|
|
|
|
154
|
|
|
|
|
|
|
my ($e_val_ref, $e_vec_ref, $original_data_ref) = $eigen->results; |
155
|
|
|
|
|
|
|
|
156
|
|
|
|
|
|
|
# Print eigenvalues along with corresponding eigenvectors. |
157
|
|
|
|
|
|
|
for my $i (0..$#{$e_val_ref}) { |
158
|
|
|
|
|
|
|
print qq{\nEigenvalue: $e_val_ref->[$i], }; |
159
|
|
|
|
|
|
|
print qq{\nEigenvector: }; |
160
|
|
|
|
|
|
|
for my $vec_component (@{$e_vec_ref->[$i]}) { print qq{$vec_component, }; }; print qq{\n}; } |
161
|
|
|
|
|
|
|
|
162
|
|
|
|
|
|
|
=head2 Math::GSL::Linalg::SVD::Matrix |
163
|
|
|
|
|
|
|
|
164
|
|
|
|
|
|
|
This namespace functions as an interface to the C C-structure typedef. |
165
|
|
|
|
|
|
|
|
166
|
|
|
|
|
|
|
=head3 new |
167
|
|
|
|
|
|
|
|
168
|
|
|
|
|
|
|
Name: new |
169
|
|
|
|
|
|
|
Implements: gsl_matrix_alloc |
170
|
|
|
|
|
|
|
Usage: $gsl_matrix_pointer_as_perl_object = Math::GSL::Linalg::SVD::Matrix->new; |
171
|
|
|
|
|
|
|
Returns: pointer to a gsl_matrix type as Perl object |
172
|
|
|
|
|
|
|
|
173
|
|
|
|
|
|
|
=head3 set_matrix |
174
|
|
|
|
|
|
|
|
175
|
|
|
|
|
|
|
Name: set_matrix |
176
|
|
|
|
|
|
|
Implements: gsl_matrix_set |
177
|
|
|
|
|
|
|
Usage: $gsl_matrix_pointer_as_perl_object->set_matrix($row, $col, $double_number); |
178
|
|
|
|
|
|
|
Returns: |
179
|
|
|
|
|
|
|
|
180
|
|
|
|
|
|
|
=head3 get_matrix |
181
|
|
|
|
|
|
|
|
182
|
|
|
|
|
|
|
Name: matrix_get |
183
|
|
|
|
|
|
|
Implements: gsl_matrix_get |
184
|
|
|
|
|
|
|
Usage: $gsl_matrix_pointer_as_perl_object->set_matrix($row, $col); |
185
|
|
|
|
|
|
|
Returns: scalar value |
186
|
|
|
|
|
|
|
|
187
|
|
|
|
|
|
|
=head3 SV_decomp |
188
|
|
|
|
|
|
|
|
189
|
|
|
|
|
|
|
Name: SV_decomp |
190
|
|
|
|
|
|
|
Implements: gsl_linalg_SV_decomp |
191
|
|
|
|
|
|
|
Usage: $gsl_matrix_pointer_as_perl_object->SV_decomp (...); |
192
|
|
|
|
|
|
|
Returns: |
193
|
|
|
|
|
|
|
|
194
|
|
|
|
|
|
|
=head3 SV_decomp_mod |
195
|
|
|
|
|
|
|
|
196
|
|
|
|
|
|
|
Name: SV_decomp_mod |
197
|
|
|
|
|
|
|
Implements: gsl_linalg_SV_decomp_mod |
198
|
|
|
|
|
|
|
Usage: $gsl_matrix_pointer_as_perl_object->SV_decomp_mod (...); |
199
|
|
|
|
|
|
|
Returns: |
200
|
|
|
|
|
|
|
|
201
|
|
|
|
|
|
|
=head3 SV_decomp_jacobi |
202
|
|
|
|
|
|
|
|
203
|
|
|
|
|
|
|
Name: SV_decomp_jacobi |
204
|
|
|
|
|
|
|
Implements: gsl_linalg_SV_decomp_jacobi |
205
|
|
|
|
|
|
|
Usage: $gsl_matrix_pointer_as_perl_object->SV_decomp_mod (...); |
206
|
|
|
|
|
|
|
Returns: |
207
|
|
|
|
|
|
|
|
208
|
|
|
|
|
|
|
=head3 Eigen_decomp |
209
|
|
|
|
|
|
|
|
210
|
|
|
|
|
|
|
Name: Eigen_decomp |
211
|
|
|
|
|
|
|
Implements: gsl_eigen_symmv |
212
|
|
|
|
|
|
|
Usage: $gsl_matrix_pointer_as_perl_object->Eigen_decomp (...); |
213
|
|
|
|
|
|
|
Returns: |
214
|
|
|
|
|
|
|
|
215
|
|
|
|
|
|
|
=head2 Math::GSL::Linalg::SVD::Vector |
216
|
|
|
|
|
|
|
|
217
|
|
|
|
|
|
|
This namespace functions as an interface to the C C-structure typedef. |
218
|
|
|
|
|
|
|
|
219
|
|
|
|
|
|
|
=head3 new |
220
|
|
|
|
|
|
|
|
221
|
|
|
|
|
|
|
Name: new |
222
|
|
|
|
|
|
|
Implements: gsl_vector_alloc |
223
|
|
|
|
|
|
|
Usage: $gsl_vector_pointer_as_perl_object = Math::GSL::Linalg::SVD::Vector->new; |
224
|
|
|
|
|
|
|
Returns: pointer to gsl_vector as perl object |
225
|
|
|
|
|
|
|
|
226
|
|
|
|
|
|
|
|
227
|
|
|
|
|
|
|
=head3 set_vector |
228
|
|
|
|
|
|
|
|
229
|
|
|
|
|
|
|
Name: vector_set |
230
|
|
|
|
|
|
|
Implements: gsl_vector_set |
231
|
|
|
|
|
|
|
Usage: $gsl_vector_pointer_as_perl_object->set_vector($row, $col, $double_number); |
232
|
|
|
|
|
|
|
Returns: |
233
|
|
|
|
|
|
|
|
234
|
|
|
|
|
|
|
=head3 get_vector |
235
|
|
|
|
|
|
|
|
236
|
|
|
|
|
|
|
Name: vector_get |
237
|
|
|
|
|
|
|
Implements: gsl_vector_get |
238
|
|
|
|
|
|
|
Usage: $gsl_vector_pointer_as_perl_object->set_vector($row, $col) |
239
|
|
|
|
|
|
|
Returns: scalar value |
240
|
|
|
|
|
|
|
|
241
|
|
|
|
|
|
|
=head2 Math::GSL::Linalg::SVD::Eigen |
242
|
|
|
|
|
|
|
|
243
|
|
|
|
|
|
|
This namespace functions as an interface to the C C-structure typedef used as workspace for |
244
|
|
|
|
|
|
|
the eigen decomposition routines of the gsl library. |
245
|
|
|
|
|
|
|
|
246
|
|
|
|
|
|
|
=head3 new |
247
|
|
|
|
|
|
|
|
248
|
|
|
|
|
|
|
Name: new |
249
|
|
|
|
|
|
|
Implements: gsl_eigen_symmv_alloc |
250
|
|
|
|
|
|
|
Usage: $gsl_vector_pointer_as_perl_object = Math::GSL::Linalg::SVD::vector->new; |
251
|
|
|
|
|
|
|
Returns: pointer to gsl_eigen_symmv type as perl object |
252
|
|
|
|
|
|
|
|
253
|
|
|
|
|
|
|
=cut |
254
|
|
|
|
|
|
|
|
255
|
|
|
|
|
|
|
sub new { |
256
|
0
|
|
|
0
|
|
|
my ( $class, $h_ref ) = @_; |
257
|
0
|
0
|
0
|
|
|
|
croak qq{\nArguments must be passed as HASH reference.} if ( ( $h_ref ) && ( ref $h_ref ne q{HASH} ) ); |
258
|
0
|
0
|
0
|
|
|
|
my $verbose = 1 if ( ( exists $h_ref->{verbose} ) && ( $h_ref->{verbose} == 1 ) ); |
259
|
0
|
|
|
|
|
|
my $self = {}; |
260
|
0
|
|
|
|
|
|
$self->{flags}{verbose} = $verbose; |
261
|
0
|
|
|
|
|
|
bless $self, $class; |
262
|
0
|
|
|
|
|
|
return $self; |
263
|
|
|
|
|
|
|
} |
264
|
|
|
|
|
|
|
|
265
|
|
|
|
|
|
|
sub load_data { |
266
|
0
|
|
|
0
|
|
|
my ( $self, $h_ref ) = @_; |
267
|
0
|
0
|
0
|
|
|
|
croak qq{\nArguments must be passed as HASH reference.} if ( ( $h_ref ) && ( ref $h_ref ne q{HASH} ) ); |
268
|
0
|
|
|
|
|
|
my $data_dirty = $h_ref->{data}; |
269
|
0
|
|
|
|
|
|
&_data_checks($data_dirty); |
270
|
0
|
|
|
|
|
|
my $data = _deep_copy_references($data_dirty); |
271
|
|
|
|
|
|
|
|
272
|
0
|
|
|
|
|
|
my $A_m = scalar ( @{$data} ); |
|
0
|
|
|
|
|
|
|
273
|
0
|
|
|
|
|
|
my $A_n = scalar ( @{$data->[0]} ); |
|
0
|
|
|
|
|
|
|
274
|
|
|
|
|
|
|
|
275
|
0
|
0
|
|
|
|
|
print qq{\nData entry matrix is: m = $A_m * n = $A_n. Feeding data to object.} if $self->{flags}{verbose}; |
276
|
|
|
|
|
|
|
|
277
|
0
|
|
|
|
|
|
$self->{data} = $data; |
278
|
0
|
|
|
|
|
|
$self->{A_m} = $A_m; |
279
|
0
|
|
|
|
|
|
$self->{A_n} = $A_n; |
280
|
|
|
|
|
|
|
|
281
|
|
|
|
|
|
|
#y set flag |
282
|
0
|
|
|
|
|
|
$self->{flags}{load} = 1; |
283
|
|
|
|
|
|
|
|
284
|
0
|
|
|
|
|
|
return; |
285
|
|
|
|
|
|
|
} |
286
|
|
|
|
|
|
|
|
287
|
|
|
|
|
|
|
sub _deep_copy_references { |
288
|
0
|
|
|
0
|
|
|
my $ref = shift; |
289
|
0
|
0
|
|
|
|
|
if (!ref $ref) { $ref; } |
|
0
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
290
|
0
|
|
|
|
|
|
elsif (ref $ref eq q{ARRAY} ) { [ map { _deep_copy_references($_) } @{$ref} ]; } |
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
291
|
0
|
|
|
|
|
|
elsif (ref $ref eq q{HASH} ) { + { map { $_ => _deep_copy_references($ref->{$_}) } (keys %{$ref}) }; } |
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
292
|
0
|
|
|
|
|
|
else { die "what type is $_?" } |
293
|
|
|
|
|
|
|
} |
294
|
|
|
|
|
|
|
# don´t return on this one - kills its before recursion - over-kill. its only operating on a LoL |
295
|
|
|
|
|
|
|
|
296
|
|
|
|
|
|
|
#/ just a sub not a method |
297
|
|
|
|
|
|
|
sub _data_checks { |
298
|
0
|
|
|
0
|
|
|
my $data_a_ref = shift; |
299
|
0
|
|
|
|
|
|
my $rows = scalar ( @{$data_a_ref} ); |
|
0
|
|
|
|
|
|
|
300
|
0
|
0
|
0
|
|
|
|
croak qq{\nI need some data - there are too few rows in your data.\n} if ( !$rows || $rows == 1 ); |
301
|
0
|
|
|
|
|
|
my $cols = scalar ( @{$data_a_ref->[0]} ); |
|
0
|
|
|
|
|
|
|
302
|
0
|
0
|
0
|
|
|
|
croak qq{\nI need some data - there are too few columns in your data.\n} if ( !$cols || $cols == 1 ); |
303
|
0
|
|
|
|
|
|
for my $row (@{$data_a_ref}) { |
|
0
|
|
|
|
|
|
|
304
|
0
|
0
|
|
|
|
|
croak qq{\n\nData set must be passed as ARRAY references.\n} if ( ref $row ne q{ARRAY} ); |
305
|
0
|
0
|
|
|
|
|
croak qq{\n\nAll rows must have the same number of columns.\n} if ( scalar( @{$row} ) != $cols ); |
|
0
|
|
|
|
|
|
|
306
|
|
|
|
|
|
|
} |
307
|
0
|
|
|
|
|
|
return; |
308
|
|
|
|
|
|
|
} |
309
|
|
|
|
|
|
|
|
310
|
|
|
|
|
|
|
#/////////////////////////////////////////////////////// ANALYSIS ///////////////////////////////////////////////////// |
311
|
|
|
|
|
|
|
|
312
|
|
|
|
|
|
|
sub decompose { |
313
|
0
|
|
|
0
|
|
|
my ( $self, $h_ref ) = @_; |
314
|
0
|
0
|
|
|
|
|
croak qq{\nYou need to load some data first.} if ( !exists $self->{flags}{load}); |
315
|
0
|
0
|
0
|
|
|
|
croak qq{\nArguments must be passed as HASH reference.} if ( ( $h_ref ) && ( ref $h_ref ne q{HASH} ) ); |
316
|
|
|
|
|
|
|
# Golub-Reinsch / modified Golub-Reinsch / one-sided Jacobi orthogonalization |
317
|
0
|
0
|
|
|
|
|
exists $h_ref->{algorithm} || print qq{\nUsing default Golub-Reinsch algorithm for SVD.}; |
318
|
0
|
0
|
|
|
|
|
my $sdv = exists $h_ref->{algorithm} ? $h_ref->{algorithm} : q{gd}; |
319
|
0
|
0
|
|
|
|
|
if ( $sdv eq q{eigen} ) { $self->_eigen } |
|
0
|
|
|
|
|
|
|
320
|
0
|
|
|
|
|
|
else { $self->_svd($sdv) }; |
321
|
0
|
|
|
|
|
|
return; |
322
|
|
|
|
|
|
|
} |
323
|
|
|
|
|
|
|
|
324
|
|
|
|
|
|
|
sub _svd { |
325
|
0
|
|
|
0
|
|
|
my ( $self, $sdv ) = @_; |
326
|
|
|
|
|
|
|
#y sdv specific check |
327
|
0
|
|
|
|
|
|
$self->_sdv_check(); |
328
|
|
|
|
|
|
|
|
329
|
|
|
|
|
|
|
#croak qq{\nI don\x27t recognise that value for the \x27algorithm\x27 option - requires \x27gd\x27, \x27mod\x27 } |
330
|
|
|
|
|
|
|
# . qq{or \x27jacobi\x27 (defaults#to \x27gs\x27 without option).} if ( $sdv !~ /\A(gd|mod|jacobi)\z/xms ); |
331
|
|
|
|
|
|
|
|
332
|
0
|
|
|
|
|
|
my $data = $self->{data}; |
333
|
0
|
|
|
|
|
|
my $A_m = $self->{A_m}; |
334
|
0
|
|
|
|
|
|
my $A_n = $self->{A_n}; |
335
|
|
|
|
|
|
|
|
336
|
0
|
|
|
|
|
|
my $A_mat = Math::GSL::Linalg::SVD::Matrix->new($A_m,$A_n); |
337
|
|
|
|
|
|
|
|
338
|
|
|
|
|
|
|
#y only if we have mod algorithm |
339
|
|
|
|
|
|
|
#my $workspace_mat = Math::GSL::Linalg::SVD::Matrix->new($A_n,$A_n) if ( $sdv eq q{mod} ); |
340
|
|
|
|
|
|
|
|
341
|
0
|
|
|
|
|
|
my $V_mat = Math::GSL::Linalg::SVD::Matrix->new($A_n,$A_n); |
342
|
0
|
|
|
|
|
|
my $S_vec = Math::GSL::Linalg::SVD::Vector->new($A_n); |
343
|
|
|
|
|
|
|
|
344
|
|
|
|
|
|
|
#y only needed if either gd or mod - thus either put if postfix here or put in twice in the anonysubs below |
345
|
0
|
0
|
0
|
|
|
|
my $workspace_vec = Math::GSL::Linalg::SVD::Vector->new($A_n) if ( ( $sdv eq q{gd} ) || ( $sdv eq q{mod} ) ); |
346
|
|
|
|
|
|
|
|
347
|
|
|
|
|
|
|
#$self->{C_objects} = { A => $A_mat, V => $V_mat, S => $S_vec, workspace_vector => $workspace_vec, }; |
348
|
|
|
|
|
|
|
|
349
|
|
|
|
|
|
|
#/ load matrix - as private sub not method |
350
|
0
|
|
|
|
|
|
&_load_matrix($A_mat, $data, $A_m, $A_n); |
351
|
|
|
|
|
|
|
|
352
|
0
|
|
|
0
|
|
|
my %algo = ( gd => sub { my $int = $A_mat->SV_decomp( $V_mat, $S_vec, $workspace_vec ); }, |
353
|
0
|
0
|
|
0
|
|
|
mod => sub { my $workspace_mat = Math::GSL::Linalg::SVD::Matrix->new($A_n,$A_n) if ( $sdv eq q{mod} ); |
354
|
0
|
|
|
|
|
|
my $int = $A_mat->SV_decomp_mod( $workspace_mat, $V_mat, $S_vec, $workspace_vec ); }, |
355
|
0
|
|
|
0
|
|
|
jacobi => sub { my $int = $A_mat->SV_decomp_jacobi($V_mat, $S_vec ); }, |
356
|
0
|
|
|
|
|
|
); |
357
|
|
|
|
|
|
|
|
358
|
0
|
|
|
|
|
|
my $type = $algo{$sdv}; |
359
|
0
|
0
|
|
|
|
|
croak qq{\nI don\x27t recognise that value for the \x27algorithm\x27 option - requires \x27gd\x27, \x27mod\x27 } |
360
|
|
|
|
|
|
|
. qq{or \x27jacobi\x27 (defaults#to \x27gs\x27 without option).} if ( !defined $type ); |
361
|
|
|
|
|
|
|
#if ( $sdv !~ /\A(gd|mod|jacobi)\z/xms ); |
362
|
|
|
|
|
|
|
|
363
|
0
|
|
|
|
|
|
$type->(); |
364
|
|
|
|
|
|
|
|
365
|
0
|
|
|
|
|
|
my $d = []; |
366
|
0
|
|
|
|
|
|
$d = &_get_d ($S_vec, $A_n); |
367
|
|
|
|
|
|
|
|
368
|
|
|
|
|
|
|
#/ no method call on thingy so need to put the data directly into the object here |
369
|
0
|
|
|
|
|
|
$self->{d} = $d; |
370
|
|
|
|
|
|
|
|
371
|
|
|
|
|
|
|
#/ get U that is actually same matrix as A but with U data now - so its just as well we pre-stored the data as the original is gone |
372
|
0
|
|
|
|
|
|
my $U = []; |
373
|
0
|
|
|
|
|
|
$U = &_get_u ($A_mat, $A_m, $A_n); |
374
|
0
|
|
|
|
|
|
$self->{U} = $U; |
375
|
|
|
|
|
|
|
|
376
|
|
|
|
|
|
|
#/ get the V matrix |
377
|
0
|
|
|
|
|
|
my $V = []; |
378
|
0
|
|
|
|
|
|
$V = &_get_u ($V_mat, $A_n, $A_n); |
379
|
0
|
|
|
|
|
|
$self->{V} = $V; |
380
|
|
|
|
|
|
|
|
381
|
|
|
|
|
|
|
#y set flag for results |
382
|
0
|
|
|
|
|
|
$self->{flags}{svd} = 1; |
383
|
|
|
|
|
|
|
|
384
|
0
|
|
|
|
|
|
return; |
385
|
|
|
|
|
|
|
} |
386
|
|
|
|
|
|
|
|
387
|
|
|
|
|
|
|
sub _eigen { |
388
|
0
|
|
|
0
|
|
|
my $self = shift; |
389
|
0
|
|
|
|
|
|
$self->_eigen_check; |
390
|
0
|
|
|
|
|
|
my $data = $self->{data}; |
391
|
0
|
|
|
|
|
|
my $A_m = $self->{A_m}; # checked: m == n |
392
|
|
|
|
|
|
|
|
393
|
0
|
|
|
|
|
|
my $Eigen_mat = Math::GSL::Linalg::SVD::Matrix->new($A_m,$A_m); |
394
|
0
|
|
|
|
|
|
my $EV_mat = Math::GSL::Linalg::SVD::Matrix->new($A_m,$A_m); |
395
|
0
|
|
|
|
|
|
my $EV_vec = Math::GSL::Linalg::SVD::Vector->new($A_m); |
396
|
0
|
|
|
|
|
|
my $workpace_eigen = Math::GSL::Linalg::SVD::Eigen->new(4*$A_m); |
397
|
|
|
|
|
|
|
|
398
|
0
|
|
|
|
|
|
&_load_matrix($Eigen_mat, $data, $A_m, $A_m); |
399
|
|
|
|
|
|
|
|
400
|
0
|
|
|
|
|
|
my $int = $Eigen_mat->Eigen_decomp($EV_vec, $EV_mat, $workpace_eigen ); |
401
|
|
|
|
|
|
|
|
402
|
0
|
|
|
|
|
|
my $e_vals = &_get_d ($EV_vec, $A_m); |
403
|
0
|
|
|
|
|
|
$self->{eigen_values} = $e_vals; |
404
|
|
|
|
|
|
|
|
405
|
|
|
|
|
|
|
#my $e_vecs = &_get_v ($EV_mat, $A_m); |
406
|
0
|
|
|
|
|
|
my $e_vecs = &_get_u ($EV_mat, $A_m, $A_m); |
407
|
0
|
|
|
|
|
|
$e_vecs = _transpose($e_vecs); |
408
|
0
|
|
|
|
|
|
$self->{eigen_vectors} = $e_vecs; |
409
|
|
|
|
|
|
|
|
410
|
|
|
|
|
|
|
#y set flag for results |
411
|
0
|
|
|
|
|
|
$self->{flags}{eigen} = 1; |
412
|
|
|
|
|
|
|
|
413
|
0
|
|
|
|
|
|
return; |
414
|
|
|
|
|
|
|
} |
415
|
|
|
|
|
|
|
|
416
|
|
|
|
|
|
|
# sub routine not method |
417
|
|
|
|
|
|
|
sub _transpose { |
418
|
|
|
|
|
|
|
#y basic sub |
419
|
0
|
|
|
0
|
|
|
my $a_ref = shift; |
420
|
0
|
|
|
|
|
|
my $done = []; |
421
|
0
|
|
|
|
|
|
for my $col ( 0..$#{$a_ref->[0]} ) { |
|
0
|
|
|
|
|
|
|
422
|
0
|
|
|
|
|
|
push @{$done}, [ map { $_->[$col] } @{$a_ref} ]; |
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
423
|
|
|
|
|
|
|
} |
424
|
0
|
|
|
|
|
|
return $done; |
425
|
|
|
|
|
|
|
} |
426
|
|
|
|
|
|
|
|
427
|
|
|
|
|
|
|
sub _eigen_check { |
428
|
0
|
|
|
0
|
|
|
my $self = shift; |
429
|
0
|
|
|
|
|
|
my $data = $self->{data}; |
430
|
0
|
|
|
|
|
|
my $A_m = $self->{A_m}; |
431
|
0
|
|
|
|
|
|
my $A_n = $self->{A_n}; |
432
|
|
|
|
|
|
|
# twat: |
433
|
|
|
|
|
|
|
# croak qq{\nEigen decomposition requires a square matrix.} if ( $A_m == $A_n ); |
434
|
0
|
0
|
|
|
|
|
croak qq{\nEigen decomposition requires a square matrix.} if ( $A_m != $A_n ); |
435
|
0
|
|
|
|
|
|
return; |
436
|
|
|
|
|
|
|
} |
437
|
|
|
|
|
|
|
|
438
|
|
|
|
|
|
|
sub _sdv_check { |
439
|
0
|
|
|
0
|
|
|
my $self = shift; |
440
|
0
|
|
|
|
|
|
my $data = $self->{data}; |
441
|
0
|
|
|
|
|
|
my $A_m = $self->{A_m}; |
442
|
0
|
|
|
|
|
|
my $A_n = $self->{A_n}; |
443
|
0
|
0
|
|
|
|
|
croak qq{\nSVD for matrix MxN with M $A_m); # duh... A_m >= A_n |
444
|
0
|
|
|
|
|
|
return; |
445
|
|
|
|
|
|
|
} |
446
|
|
|
|
|
|
|
|
447
|
|
|
|
|
|
|
sub _load_matrix { |
448
|
0
|
|
|
0
|
|
|
my ( $A_mat, $data, $A_m, $A_n) = @_; |
449
|
|
|
|
|
|
|
|
450
|
0
|
|
|
|
|
|
for my $row ( (0..$A_m-1) ) { |
451
|
0
|
|
|
|
|
|
for my $col ( (0..$A_n-1) ) { |
452
|
0
|
|
|
|
|
|
$A_mat->matrix_set($row,$col,$data->[$row][$col]); |
453
|
|
|
|
|
|
|
} |
454
|
|
|
|
|
|
|
} |
455
|
|
|
|
|
|
|
# return $A_mat; |
456
|
0
|
|
|
|
|
|
return; |
457
|
|
|
|
|
|
|
} |
458
|
|
|
|
|
|
|
|
459
|
|
|
|
|
|
|
sub _get_d { |
460
|
0
|
|
|
0
|
|
|
my ($S_vec, $A_n) = @_; |
461
|
0
|
|
|
|
|
|
my $d = []; |
462
|
0
|
|
|
|
|
|
for my $row ( (0..$A_n-1) ) { |
463
|
0
|
|
|
|
|
|
my $val = $S_vec->vector_get($row); |
464
|
|
|
|
|
|
|
#y nao faz diferenenca aqui... |
465
|
|
|
|
|
|
|
#$d->[$row] = $val; |
466
|
0
|
|
|
|
|
|
push @{$d}, $val; |
|
0
|
|
|
|
|
|
|
467
|
|
|
|
|
|
|
} |
468
|
|
|
|
|
|
|
#/ as sub we´ll need to return the list and not put it into the object |
469
|
|
|
|
|
|
|
#$self->{d} = $d; |
470
|
|
|
|
|
|
|
# return; |
471
|
0
|
|
|
|
|
|
return $d; |
472
|
|
|
|
|
|
|
} |
473
|
|
|
|
|
|
|
|
474
|
|
|
|
|
|
|
sub _get_u { |
475
|
|
|
|
|
|
|
#/ no need for method just private sub |
476
|
|
|
|
|
|
|
#my ($self, $S_vec, $A_n) = @_; |
477
|
|
|
|
|
|
|
#/ get U that is actually same matrix as A but with U data now - so its just as well we pre-stored the data as the original is gone |
478
|
0
|
|
|
0
|
|
|
my ($A_mat, $A_m, $A_n) = @_; |
479
|
|
|
|
|
|
|
|
480
|
0
|
|
|
|
|
|
my $U = []; |
481
|
0
|
|
|
|
|
|
for my $row ( (0..$A_m-1) ) { |
482
|
0
|
|
|
|
|
|
for my $col ( (0..$A_n-1) ) { |
483
|
0
|
|
|
|
|
|
my $val = $A_mat->matrix_get($row,$col); |
484
|
|
|
|
|
|
|
|
485
|
|
|
|
|
|
|
#/ can´t push - need to use each index individually! |
486
|
|
|
|
|
|
|
#push @{$U}, $val; |
487
|
0
|
|
|
|
|
|
$U->[$row][$col] = $val; |
488
|
|
|
|
|
|
|
} |
489
|
|
|
|
|
|
|
} |
490
|
|
|
|
|
|
|
|
491
|
|
|
|
|
|
|
#/ as sub we´ll need to return the list and not put it into the object |
492
|
|
|
|
|
|
|
#$self->{d} = $d; |
493
|
|
|
|
|
|
|
# return; |
494
|
0
|
|
|
|
|
|
return $U; |
495
|
|
|
|
|
|
|
} |
496
|
|
|
|
|
|
|
|
497
|
|
|
|
|
|
|
sub results { |
498
|
0
|
|
|
0
|
|
|
my $self = shift; |
499
|
0
|
0
|
|
|
|
|
croak qq{\nThis method is called in LIST context.} if !wantarray; |
500
|
0
|
0
|
0
|
|
|
|
if ( ( $self->{flags}{svd} ) && ( $self->{flags}{eigen} ) ) { |
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
501
|
0
|
|
|
|
|
|
croak qq{\nYou performed both SVD and Eigen decomp. Which did you want to do?}; |
502
|
|
|
|
|
|
|
} |
503
|
|
|
|
|
|
|
elsif ( $self->{flags}{svd} ) { |
504
|
0
|
0
|
|
|
|
|
print qq{\nPassing SVD results data.} if $self->{flags}{verbose}; |
505
|
0
|
|
|
|
|
|
return $self->{d}, $self->{U}, $self->{V}, $self->{data}; |
506
|
|
|
|
|
|
|
} |
507
|
|
|
|
|
|
|
# silly, but... |
508
|
|
|
|
|
|
|
elsif ( $self->{flags}{eigen} ) { |
509
|
0
|
0
|
|
|
|
|
print qq{\nPassing Eigen results data.} if $self->{flags}{verbose}; |
510
|
0
|
|
|
|
|
|
return $self->{eigen_values}, $self->{eigen_vectors}, $self->{data}; |
511
|
|
|
|
|
|
|
} |
512
|
|
|
|
|
|
|
# perlcritic plesure... |
513
|
0
|
|
|
|
|
|
return; |
514
|
|
|
|
|
|
|
} |
515
|
|
|
|
|
|
|
|
516
|
|
|
|
|
|
|
|
517
|
|
|
|
|
|
|
1; |
518
|
|
|
|
|
|
|
|
519
|
|
|
|
|
|
|
__END__ |