Skip to content

Commit b4d7576

Browse files
committed
normalized gram schmit method
1 parent b83c694 commit b4d7576

2 files changed

Lines changed: 70 additions & 25 deletions

File tree

lib/Value/Matrix.pm

Lines changed: 43 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -669,36 +669,14 @@ sub isREQ {
669669
}
670670
return $lk * $r == $rk * $l;
671671
} elsif (scalar @ldim == 2) {
672-
# get Gram-Schmidt basis for each row space
673-
my @rrows = map { $r->new($_) } $r->value;
674-
@rrows = grep { !$_->isZero } @rrows;
675-
@rrows = map { $_ / ($_ * $_)**0.5 } @rrows;
676-
my @rgs = ($rrows[0]);
677-
for my $i (1 .. $#rrows) {
678-
my $proj = $r->new((0) x $rdim[1]);
679-
for my $v (@rgs) {
680-
$proj += ($rrows[$i] * $v) * $v;
681-
}
682-
my $gs = $rrows[$i] - $proj;
683-
push @rgs, $gs / ($gs * $gs)**0.5 unless $rrows[$i] == $proj;
684-
}
685-
my @lrows = map { $l->new($_) } $l->value;
686-
@lrows = grep { !$_->isZero } @lrows;
687-
@lrows = map { $_ / ($_ * $_)**0.5 } @lrows;
688-
my @lgs = ($lrows[0]);
689-
for my $i (1 .. $#lrows) {
690-
my $proj = $r->new((0) x $ldim[1]);
691-
for my $v (@lgs) {
692-
$proj += ($lrows[$i] * $v) * $v;
693-
}
694-
my $gs = $lrows[$i] - $proj;
695-
push @lgs, $gs / ($gs * $gs)**0.5 unless $lrows[$i] == $proj;
696-
}
672+
my @rgs = $r->NGS;
673+
my @lgs = $l->NGS;
697674

698675
return 0 if scalar @lgs != scalar @rgs;
699676

700677
# project each row from $rrows onto @lgs;
701678
# if the complement is nonzero, the row spaces disagree
679+
my @rrows = map { $r->new($_) } $r->value;
702680
for my $v (@rrows) {
703681
my $proj = $r->new((0) x $ldim[1]);
704682
for my $w (@lgs) {
@@ -707,6 +685,7 @@ sub isREQ {
707685
return 0 unless $v == $proj;
708686
}
709687
# and vice versa
688+
my @lrows = map { $l->new($_) } $l->value;
710689
for my $v (@lrows) {
711690
my $proj = $r->new((0) x $rdim[1]);
712691
for my $w (@rgs) {
@@ -726,6 +705,45 @@ sub isREQ {
726705
}
727706
}
728707

708+
=head3 C<NGS>
709+
710+
Normalized Gram Schmit basis for a given list of vectors. This uses "fuzzy" equivalency to avoid machine rounding
711+
issues. If called as a method on a Matrix, the rows of that Matrix are used as the list of vectors.
712+
713+
Usage:
714+
715+
$A = Matrix([1, 2, 2], [2, 2, 1]);
716+
$A->NGS # returns [Matrix(1, 2, 2)/3, Matrix(10, 2, -7)/sqrt(153)]
717+
718+
Value::Matrix->NGS([1, 2, 2], [2, 2, 1]) #returns the same as above
719+
720+
=cut
721+
722+
sub NGS {
723+
my ($self, @rows) = @_;
724+
@rows = $self->value if !@rows && ref($self) eq 'Value::Matrix';
725+
Value::Error('You must provide vectors to apply Gram Schmit to') if !@rows;
726+
@rows = map { Value::Matrix->new($_) } @rows;
727+
@rows = grep { !$_->isZero } @rows;
728+
Value::Error('You must provide at least one nonzero row for Gram Schmit') unless @rows;
729+
my $n = ($rows[0]->dimensions)[0];
730+
for my $r (@rows) {
731+
Value::Error('Rows provided for Gram Schmit should not be nested arrays') unless $r->degree == 1;
732+
Value::Error('All rows provided for Gram Schmit should have the same length') unless ($r->dimensions)[0] == $n;
733+
}
734+
@rows = map { $_ / ($_ * $_)**0.5 } @rows;
735+
my @gs = ($rows[0]);
736+
for my $i (1 .. $#rows) {
737+
my $proj = Value::Matrix->new((0) x $n);
738+
for my $v (@gs) {
739+
$proj += ($rows[$i] * $v) * $v;
740+
}
741+
my $gs = $rows[$i] - $proj;
742+
push @gs, $gs / ($gs * $gs)**0.5 unless $rows[$i] == $proj;
743+
}
744+
return @gs;
745+
}
746+
729747
sub _isNumber {
730748
my $n = shift;
731749
return Value::isNumber($n) || Value::classMatch($n, 'Fraction');

t/math_objects/matrix.t

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -224,6 +224,33 @@ subtest 'Check if two matrices are (fuzzy) row equivalent' => sub {
224224

225225
};
226226

227+
subtest 'Normalized Gram Schmit' => sub {
228+
my @A = ([ 1, 2, 2 ], [ 2, 2, 1 ]);
229+
my $A = Matrix(@A);
230+
my @NGSA = $A->NGS;
231+
my @NGSVMA = Value::Matrix->NGS(@A);
232+
233+
is "@NGSA", "[0.333333,0.666667,0.666667] [0.808452,0.16169,-0.565916]",
234+
'Test that NGS finds a correct normalized GS basis';
235+
is "@NGSVMA", "[0.333333,0.666667,0.666667] [0.808452,0.16169,-0.565916]",
236+
'Test that NGS finds a correct normalized GS basis';
237+
238+
like dies {
239+
Value::Matrix->NGS();
240+
}, qr/You must provide vectors to apply Gram Schmit to/, 'Test that you must pass something as an argument';
241+
like dies {
242+
Value::Matrix->NGS([ 0, 0 ], [ 0, 10**-16 ]);
243+
}, qr/You must provide at least one nonzero row for Gram Schmit/,
244+
'Test that you must pass at least one nonzero row';
245+
like dies {
246+
Value::Matrix->NGS([ [ 1, 0 ], [ 1, 0 ] ]);
247+
}, qr/Rows provided for Gram Schmit should not be nested arrays/,
248+
'Test that rows are rows, not nested matrices';
249+
like dies {
250+
Value::Matrix->NGS([ 1, 0 ], [ 1, 0, 0 ]);
251+
}, qr/All rows provided for Gram Schmit should have the same length/, 'Test that rows have same length';
252+
};
253+
227254
subtest 'Transpose a Matrix' => sub {
228255
my $A = Matrix([ [ 1, 2, 3, 4 ], [ 5, 6, 7, 8 ], [ 9, 10, 11, 12 ] ]);
229256
my $B = Matrix([ [ 1, 5, 9 ], [ 2, 6, 10 ], [ 3, 7, 11 ], [ 4, 8, 12 ] ]);

0 commit comments

Comments
 (0)