line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
|
2
|
|
|
|
|
|
|
=head1 NAME
|
3
|
|
|
|
|
|
|
|
4
|
|
|
|
|
|
|
Math::Orthonormalize - Gram-Schmidt Orthonormalization of vectors
|
5
|
|
|
|
|
|
|
|
6
|
|
|
|
|
|
|
=head1 SYNOPSIS
|
7
|
|
|
|
|
|
|
|
8
|
|
|
|
|
|
|
use Math::Orthonormalize qw(:all);
|
9
|
|
|
|
|
|
|
|
10
|
|
|
|
|
|
|
my @base_of_r_2 = (
|
11
|
|
|
|
|
|
|
[2, 1],
|
12
|
|
|
|
|
|
|
[1, 3]
|
13
|
|
|
|
|
|
|
);
|
14
|
|
|
|
|
|
|
my $vector = [1, 2, 3];
|
15
|
|
|
|
|
|
|
|
16
|
|
|
|
|
|
|
my @orthonormalized = orthonormalize(@base_of_r_2);
|
17
|
|
|
|
|
|
|
my @orthogonalized = orthogonalize(@base_of_r_2);
|
18
|
|
|
|
|
|
|
|
19
|
|
|
|
|
|
|
my $normalized = normalize($vector);
|
20
|
|
|
|
|
|
|
my $scaled = scale(2, $vector);
|
21
|
|
|
|
|
|
|
my $scalar = scalar_product($vector1, $vector2);
|
22
|
|
|
|
|
|
|
|
23
|
|
|
|
|
|
|
=head1 DESCRIPTION
|
24
|
|
|
|
|
|
|
|
25
|
|
|
|
|
|
|
Math::Orthonormalize offers subroutines to compute normalized or
|
26
|
|
|
|
|
|
|
non-normalized orthogonal bases of Euclidean vector spaces. That means:
|
27
|
|
|
|
|
|
|
Given a vector base of R^n, it computes a new base of R^n whose individual
|
28
|
|
|
|
|
|
|
vectors are all orthogonal. If those new base vectors all have a length of
|
29
|
|
|
|
|
|
|
1, the base is orthonormalized.
|
30
|
|
|
|
|
|
|
|
31
|
|
|
|
|
|
|
The module uses the Gram-Schmidt Algorithm.
|
32
|
|
|
|
|
|
|
|
33
|
|
|
|
|
|
|
=head2 EXPORT
|
34
|
|
|
|
|
|
|
|
35
|
|
|
|
|
|
|
No subroutines are exported by default, but the standart Exporter semantics are
|
36
|
|
|
|
|
|
|
in place, including the ':all' tag that imports all of the exportable
|
37
|
|
|
|
|
|
|
subroutines which are listed below.
|
38
|
|
|
|
|
|
|
|
39
|
|
|
|
|
|
|
=cut
|
40
|
|
|
|
|
|
|
|
41
|
|
|
|
|
|
|
package Math::Orthonormalize;
|
42
|
|
|
|
|
|
|
|
43
|
6
|
|
|
6
|
|
15991
|
use 5.006;
|
|
6
|
|
|
|
|
20
|
|
|
6
|
|
|
|
|
243
|
|
44
|
6
|
|
|
6
|
|
38
|
use strict;
|
|
6
|
|
|
|
|
10
|
|
|
6
|
|
|
|
|
207
|
|
45
|
6
|
|
|
6
|
|
29
|
use warnings;
|
|
6
|
|
|
|
|
13
|
|
|
6
|
|
|
|
|
209
|
|
46
|
|
|
|
|
|
|
|
47
|
6
|
|
|
6
|
|
1854
|
use Math::Symbolic qw/parse_from_string/;
|
|
6
|
|
|
|
|
545818
|
|
|
6
|
|
|
|
|
531
|
|
48
|
6
|
|
|
6
|
|
55
|
use Carp;
|
|
6
|
|
|
|
|
11
|
|
|
6
|
|
|
|
|
5737
|
|
49
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
our $VERSION = '1.00';
|
51
|
|
|
|
|
|
|
|
52
|
|
|
|
|
|
|
require Exporter;
|
53
|
|
|
|
|
|
|
our @ISA = qw(Exporter);
|
54
|
|
|
|
|
|
|
our %EXPORT_TAGS = ( 'all' => [ qw(
|
55
|
|
|
|
|
|
|
orthonormalize
|
56
|
|
|
|
|
|
|
orthogonalize
|
57
|
|
|
|
|
|
|
scalar_product
|
58
|
|
|
|
|
|
|
normalize
|
59
|
|
|
|
|
|
|
scale
|
60
|
|
|
|
|
|
|
) ] );
|
61
|
|
|
|
|
|
|
our @EXPORT_OK = ( @{ $EXPORT_TAGS{'all'} } );
|
62
|
|
|
|
|
|
|
our @EXPORT = ();
|
63
|
|
|
|
|
|
|
|
64
|
|
|
|
|
|
|
=head1 SUBROUTINES
|
65
|
|
|
|
|
|
|
|
66
|
|
|
|
|
|
|
=head2 orthonormalize
|
67
|
|
|
|
|
|
|
|
68
|
|
|
|
|
|
|
Takes any number (>1) of vectors (array refs of vector components) as argument
|
69
|
|
|
|
|
|
|
which form a base (that is, they are linearly independent) and returns
|
70
|
|
|
|
|
|
|
an orthogonalized and normalized base of the same vector space
|
71
|
|
|
|
|
|
|
(that is, n new array references).
|
72
|
|
|
|
|
|
|
|
73
|
|
|
|
|
|
|
=cut
|
74
|
|
|
|
|
|
|
|
75
|
|
|
|
|
|
|
sub orthonormalize {
|
76
|
3
|
|
|
3
|
1
|
2175
|
my @vectors = @_;
|
77
|
3
|
50
|
|
|
|
8
|
croak "No arguments to orthogonalize"
|
78
|
|
|
|
|
|
|
if not @vectors;
|
79
|
5
|
|
|
|
|
13
|
croak "Arguments to orthogonalize must be array refs (vectors)"
|
80
|
3
|
50
|
|
|
|
5
|
if grep {ref($_) ne 'ARRAY'} @vectors;
|
81
|
3
|
|
|
|
|
3
|
my $dim = @{$vectors[0]};
|
|
3
|
|
|
|
|
4
|
|
82
|
5
|
|
|
|
|
13
|
croak "Vectors must have same dimension for orthogonalization"
|
83
|
3
|
50
|
|
|
|
5
|
if grep {@$_ != $dim} @vectors;
|
84
|
|
|
|
|
|
|
|
85
|
3
|
|
|
|
|
6
|
my @base = orthogonalize(@vectors);
|
86
|
3
|
|
|
|
|
3
|
@base = map {normalize($_)} @base;
|
|
5
|
|
|
|
|
9
|
|
87
|
3
|
|
|
|
|
8
|
return @base;
|
88
|
|
|
|
|
|
|
}
|
89
|
|
|
|
|
|
|
|
90
|
|
|
|
|
|
|
|
91
|
|
|
|
|
|
|
=head2 orthogonalize
|
92
|
|
|
|
|
|
|
|
93
|
|
|
|
|
|
|
Takes any number (>1) of vectors (array refs of vector components) as argument
|
94
|
|
|
|
|
|
|
which form a base (that is, they are linearly independent) and returns
|
95
|
|
|
|
|
|
|
an orthogonalized base of the same vector space (that is, n new array
|
96
|
|
|
|
|
|
|
references).
|
97
|
|
|
|
|
|
|
|
98
|
|
|
|
|
|
|
=cut
|
99
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
sub orthogonalize {
|
101
|
6
|
|
|
6
|
1
|
2539
|
my @vectors = @_;
|
102
|
6
|
50
|
|
|
|
17
|
croak "No arguments to orthogonalize"
|
103
|
|
|
|
|
|
|
if not @vectors;
|
104
|
10
|
|
|
|
|
27
|
croak "Arguments to orthogonalize must be array refs (vectors)"
|
105
|
6
|
50
|
|
|
|
9
|
if grep {ref($_) ne 'ARRAY'} @vectors;
|
106
|
6
|
|
|
|
|
7
|
my $dim = @{$vectors[0]};
|
|
6
|
|
|
|
|
9
|
|
107
|
10
|
|
|
|
|
25
|
croak "Vectors must have same dimension for orthogonalization"
|
108
|
6
|
50
|
|
|
|
6
|
if grep {@$_ != $dim} @vectors;
|
109
|
|
|
|
|
|
|
|
110
|
6
|
|
|
|
|
8
|
my @newbase;
|
111
|
6
|
|
|
|
|
9
|
push @newbase, $vectors[0];
|
112
|
6
|
|
|
|
|
8
|
my @squares;
|
113
|
6
|
100
|
|
|
|
19
|
push @squares, scalar_product($vectors[0], $vectors[0]) if @vectors > 1;
|
114
|
6
|
|
|
|
|
13
|
foreach my $i (1..$#vectors) {
|
115
|
4
|
|
|
|
|
5
|
my $new = $vectors[$i];
|
116
|
4
|
|
|
|
|
7
|
foreach my $j (0..$#newbase) {
|
117
|
4
|
|
|
|
|
8
|
my $vec = scale(
|
118
|
|
|
|
|
|
|
scalar_product($vectors[$i], $newbase[$j])
|
119
|
|
|
|
|
|
|
/ $squares[$j],
|
120
|
|
|
|
|
|
|
$newbase[$j]
|
121
|
|
|
|
|
|
|
);
|
122
|
4
|
|
|
|
|
8
|
@$new = map {$_ -= shift @$vec} @$new;
|
|
8
|
|
|
|
|
25
|
|
123
|
|
|
|
|
|
|
}
|
124
|
4
|
|
|
|
|
22
|
push @newbase, $new;
|
125
|
4
|
50
|
|
|
|
13
|
push @squares, scalar_product($new, $new) if $i < $#vectors;
|
126
|
|
|
|
|
|
|
}
|
127
|
6
|
|
|
|
|
21
|
return @newbase;
|
128
|
|
|
|
|
|
|
}
|
129
|
|
|
|
|
|
|
|
130
|
|
|
|
|
|
|
=head2 normalize
|
131
|
|
|
|
|
|
|
|
132
|
|
|
|
|
|
|
Normalizes a vector. That is, it changes the vector length to 1 without
|
133
|
|
|
|
|
|
|
changing the vector's direction.
|
134
|
|
|
|
|
|
|
|
135
|
|
|
|
|
|
|
Takes an array reference with the vector components as argument and returns
|
136
|
|
|
|
|
|
|
a new array reference containing the normalized vector components.
|
137
|
|
|
|
|
|
|
|
138
|
|
|
|
|
|
|
=cut
|
139
|
|
|
|
|
|
|
|
140
|
|
|
|
|
|
|
sub normalize {
|
141
|
9
|
|
|
9
|
1
|
2919
|
my $v = shift;
|
142
|
9
|
50
|
|
|
|
32
|
croak "Argument to normalize() must be an array ref (vector)"
|
143
|
|
|
|
|
|
|
if not ref($v) eq 'ARRAY';
|
144
|
9
|
50
|
|
|
|
22
|
croak "Cannot normalize 0-dimensional vectors"
|
145
|
|
|
|
|
|
|
if @$v == 0;
|
146
|
9
|
|
|
|
|
32
|
my $sum = $v->[0]**2;
|
147
|
9
|
|
|
|
|
35
|
$sum += $v->[$_]**2 for 1..$#$v;
|
148
|
9
|
|
|
|
|
31
|
$sum = sqrt($sum);
|
149
|
9
|
100
|
|
|
|
278
|
croak "Cannot normalize 0-vector"
|
150
|
|
|
|
|
|
|
if $sum == 0;
|
151
|
8
|
|
|
|
|
11
|
return [map {$_ / $sum} @$v];
|
|
17
|
|
|
|
|
47
|
|
152
|
|
|
|
|
|
|
}
|
153
|
|
|
|
|
|
|
|
154
|
|
|
|
|
|
|
=head2 scale
|
155
|
|
|
|
|
|
|
|
156
|
|
|
|
|
|
|
Takes a scalar and a vector (array reference of vector components) as
|
157
|
|
|
|
|
|
|
arguments. Multiplies every component of the vector by the specified
|
158
|
|
|
|
|
|
|
scalar and returns a new array reference containing the scaled vector
|
159
|
|
|
|
|
|
|
components.
|
160
|
|
|
|
|
|
|
|
161
|
|
|
|
|
|
|
=cut
|
162
|
|
|
|
|
|
|
|
163
|
|
|
|
|
|
|
sub scale {
|
164
|
8
|
50
|
33
|
8
|
1
|
20848
|
croak "scale() takes a scalar and a vector (ary ref) as arguments"
|
165
|
|
|
|
|
|
|
if not @_ == 2 or not ref($_[1]) eq 'ARRAY';
|
166
|
8
|
|
|
|
|
26
|
croak "Cannot handle 0-dimensional vectors"
|
167
|
8
|
50
|
|
|
|
11
|
if @{$_[1]} == 0;
|
168
|
8
|
|
|
|
|
12
|
my $new = [];
|
169
|
8
|
|
|
|
|
12
|
foreach (@{$_[1]}) {
|
|
8
|
|
|
|
|
21
|
|
170
|
19
|
|
|
|
|
144
|
push @$new, $_[0] * $_;
|
171
|
|
|
|
|
|
|
}
|
172
|
8
|
|
|
|
|
85
|
return $new;
|
173
|
|
|
|
|
|
|
}
|
174
|
|
|
|
|
|
|
|
175
|
|
|
|
|
|
|
=head2 scalar_product
|
176
|
|
|
|
|
|
|
|
177
|
|
|
|
|
|
|
Computes the scalar product of two vectors. Expects two array references
|
178
|
|
|
|
|
|
|
with vector components (same number of components) as argument and
|
179
|
|
|
|
|
|
|
returns their scalar product.
|
180
|
|
|
|
|
|
|
|
181
|
|
|
|
|
|
|
=cut
|
182
|
|
|
|
|
|
|
|
183
|
|
|
|
|
|
|
sub scalar_product {
|
184
|
17
|
50
|
|
17
|
1
|
204153
|
croak "Invalid number of arguments to scalar_product()"
|
185
|
|
|
|
|
|
|
if not @_ == 2;
|
186
|
17
|
|
|
|
|
31
|
my ($v1, $v2) = @_;
|
187
|
17
|
100
|
|
|
|
576
|
croak "Cannot compute the scalar product of vectors of different ",
|
188
|
|
|
|
|
|
|
"dimensions"
|
189
|
|
|
|
|
|
|
if @$v1 != @$v2;
|
190
|
14
|
100
|
|
|
|
236
|
croak "Cannot deal with 0-dimensional vectors"
|
191
|
|
|
|
|
|
|
if @$v1 == 0;
|
192
|
13
|
|
|
|
|
156
|
my $sum = $v1->[0] * $v2->[0];
|
193
|
13
|
100
|
|
|
|
154
|
return $sum if @$v1 == 1;
|
194
|
|
|
|
|
|
|
|
195
|
12
|
|
|
|
|
57
|
$sum += $v1->[$_] * $v2->[$_] for 1..$#$v1;
|
196
|
12
|
|
|
|
|
540
|
return $sum;
|
197
|
|
|
|
|
|
|
}
|
198
|
|
|
|
|
|
|
|
199
|
|
|
|
|
|
|
|
200
|
|
|
|
|
|
|
1;
|
201
|
|
|
|
|
|
|
__END__
|