File Coverage

blib/lib/Math/SparseMatrix.pm
Criterion Covered Total %
statement 107 134 79.8
branch 26 50 52.0
condition 8 24 33.3
subroutine 10 11 90.9
pod 1 7 14.2
total 152 226 67.2


line stmt bran cond sub pod time code
1             package Math::SparseMatrix;
2              
3             =head1 NAME
4              
5             Math::SparseMatrix - Provides basic sparse matrix operations such as creation, reading from file, reading transpose from file and writing to file.
6              
7             =cut
8              
9 1     1   25288 use 5.006;
  1         3  
  1         32  
10 1     1   5 use strict;
  1         2  
  1         28  
11 1     1   4 use warnings;
  1         5  
  1         48  
12              
13             require Exporter;
14             require Math::SparseVector;
15              
16 1     1   784 use Math::SparseVector;
  1         5184  
  1         2071  
17              
18              
19             our @ISA = qw(Exporter);
20              
21             # Items to export into callers namespace by default. Note: do not export
22             # names by default without a very good reason. Use EXPORT_OK instead.
23             # Do not simply export all your public functions/methods/constants.
24              
25             # This allows declaration use Math::SparseMatrix ':all';
26             # If you do not need this, moving things directly into @EXPORT or @EXPORT_OK
27             # will save memory.
28             our %EXPORT_TAGS = ( 'all' => [ qw(
29            
30             ) ] );
31              
32             our @EXPORT_OK = ( @{ $EXPORT_TAGS{'all'} } );
33              
34             our @EXPORT = qw(
35            
36             );
37              
38             our $VERSION = '0.03';
39              
40             sub new {
41 3     3 1 3390 my $class = shift;
42 3         6 my $rows = shift;
43 3         7 my $cols = shift;
44              
45 3 50 33     21 if (!defined $rows || !defined $cols) {
46 0         0 die "Math::SparseMatrix->new error.\n" .
47             "USAGE: my \$spmatrix = Math::SparseMatrix->new(\$num_rows, \$num_cols);\n";
48             }
49              
50 3         90 my $self = {
51             _rows => $rows,
52             _cols => $cols,
53             _nnz => 0,
54             _data => {}
55             };
56              
57 3         12 bless $self, $class;
58 3         28 return $self;
59             }
60              
61             sub set {
62 21     21 0 397 my $self = shift;
63 21         33 my ($row, $col, $val) = @_;
64              
65 21 50 33     100 if (!defined $row || !defined $col || !defined $val) {
      33        
66 0         0 die "Math::SparseMatrix->set error.\n" .
67             "USAGE: \$spmatrix->set(\$row, \$col, \$val);\n";
68             }
69              
70 21 50 33     109 if ($row < 1 || $row > $self->{_rows}) {
71 0         0 die "Math::SparseMatrix->set error.\n" .
72             "Row index out of bounds, must be between 1 and " .
73             $self->{_rows} . " inclusive.\n";
74             }
75            
76 21 50 33     81 if ($col < 1 || $col > $self->{_cols}) {
77 0         0 die "Math::SparseMatrix->set error.\n" .
78             "Column index out of bounds, must be between 1 and " .
79             $self->{_cols} . " inclusive.\n";
80             }
81              
82 21 50       44 if ($val == 0) {
83 0         0 die "Math::SparseMatrix->set error.\n" .
84             "Cannot store zero value in a sparse matrix.\n";
85             }
86              
87 21 100       52 if (exists $self->{_data}->{$row}) {
88             # update the number of non-zero elements in the matrix
89 2         10 $self->{_nnz} -= scalar($self->{_data}->{$row}->keys);
90 2         25 $self->{_data}{$row}->set($col,$val);
91 2         19 $self->{_nnz} += scalar($self->{_data}->{$row}->keys);
92             } else {
93 19         62 $self->{_data}->{$row} = new Math::SparseVector();
94 19         181 $self->{_data}->{$row}->set($col,$val);
95 19         232 $self->{_nnz}++;
96             }
97             }
98              
99             sub get {
100 81     81 0 16012 my $self = shift;
101 81         105 my ($row, $col) = @_;
102              
103 81 50 33     305 if (!defined $row || !defined $col) {
104 0         0 die "Math::SparseMatrix->get error.\n" .
105             "USAGE: \$val = \$spmatrix->get(\$row, \$col);\n";
106             }
107              
108 81 50 33     340 if ($row < 1 || $row > $self->{_rows}) {
109 0         0 die "Math::SparseMatrix->get error.\n" .
110             "Row index out of bounds, must be between 1 and " .
111             $self->{_rows} . " inclusive.\n";
112             }
113            
114 81 50 33     304 if ($col < 1 || $col > $self->{_cols}) {
115 0         0 die "Math::SparseMatrix->get error.\n" .
116             "Column index out of bounds, must be between 1 and " .
117             $self->{_cols} . " inclusive.\n";
118             }
119              
120 81 50       193 if (exists $self->{_data}->{$row}) {
121 81         268 return $self->{_data}->{$row}->get($col);
122             } else {
123 0         0 return 0;
124             }
125             }
126              
127             sub createFromFile {
128              
129 1     1 0 8 my $class = shift;
130 1         3 my $infile = shift;
131              
132 1 50       5 if (!defined $infile) {
133 0         0 die "Math::SparseMatrix->createFromFile error.\n" .
134             "USAGE: my \$spmatrix = Math::SparseMatrix->createFromFile(\$input_file);\n";
135             }
136              
137 1 50       18 if (!-f $infile) {
138 0         0 die "Math::SparseMatrix->createFromFile error.\n" .
139             "Cannot find file $infile.\n";
140             }
141              
142 1         6 my $self = {
143             _rows => 0,
144             _cols => 0,
145             _nnz => 0,
146             _data => {}
147             };
148              
149 1 50       35 open(INPUT, "< $infile") or
150             die "Math::SparseMatrix->createFromFile error.\n" .
151             "Failed to open sparse matrix input file $infile.\n";
152              
153             # read the number of rows, columns and the non-zero elements in the
154             # file
155 1         16 my $line = ;
156 1         3 chomp $line;
157 1         10 my ($rows, $cols, $nnz) = split / +/, $line;
158 1         29 $self->{_rows} = $rows;
159 1         3 $self->{_cols} = $cols;
160             # DO NOT SET THE NUMBER OF NON-ZEROS, THAT IS MANAGED BY THE set(...)
161             # function, which is called below
162              
163             # create a Math::SparseMatrix object
164 1         4 bless $self, $class;
165              
166             # also do error checks on file integrity
167 1         2 my $lineNum = 0;
168 1         2 my $nnzFound = 0;
169 1         6 while ($line = ) {
170 5         23 chomp $line;
171 5         5 $lineNum++;
172 5 50       30 if ($lineNum > $rows) {
173 0         0 die "Math::SparseMatrix->createFromFile error.\n".
174             "Number of rows in $infile is greater than that " .
175             "mentioned in the file header ($rows).";
176             }
177              
178 5         23 my @tokens = split / +/, $line;
179 5         8 my $index;
180             # process the pairs in each line
181 5         17 for ($index = 0; $index < @tokens; $index += 2) {
182             # the column number of the current pair is the column
183             # number, and the current line number is the row number
184 5         16 $self->set($lineNum, $tokens[$index], $tokens[$index + 1]);
185 5         33 $nnzFound++;
186             }
187             }
188              
189 1 50       5 if ($nnzFound < $nnz) {
190 0         0 die "Math::SparseMatrix->createFromFile error.\n".
191             "Number of non-zero elements found in $infile is less than that " .
192             "mentioned in the file header -- $nnzFound < $nnz.\n";
193             }
194              
195 1 50       3 if ($nnzFound > $nnz) {
196 0         0 die "Math::SparseMatrix->createFromFile error.\n".
197             "Number of non-zero elements found in $infile is greater than that " .
198             "mentioned in the file header -- $nnzFound > $nnz.\n";
199             }
200              
201             # now the transposed matrix is loaded and ready to be returned
202 1         4 return $self;
203             }
204              
205             sub createTransposeFromFile {
206              
207 1     1 0 5 my $class = shift;
208 1         2 my $infile = shift;
209              
210 1 50       4 if (!defined $infile) {
211 0         0 die "Math::SparseMatrix->createTransposeFromFile error.\n" .
212             "USAGE: \$my \$spmatrix = Math::SparseMatrix->createTransposeFromFile(\$input_file)\n";
213             }
214              
215 1 50       40 if (!-f $infile) {
216 0         0 die "Math::SparseMatrix->createTransposeFromFile error.\n" .
217             "Cannot find file $infile.\n";
218             }
219              
220 1         5 my $self = {
221             _rows => 0,
222             _cols => 0,
223             _nnz => 0,
224             _data => {}
225             };
226              
227 1 50       95 open(INPUT, "< $infile") or
228             die "Math::SparseMatrix->createTransposeFromFile error.\n" .
229             "Failed to open sparse matrix input file $infile.\n";
230              
231             # read the number of rows, columns and the non-zero elements in the
232             # file
233 1         21 my $line = ;
234 1         4 chomp $line;
235 1         8 my ($rows, $cols, $nnz) = split / +/, $line;
236             # swap number of rows and columns
237 1         5 $self->{_rows} = $cols;
238 1         2 $self->{_cols} = $rows;
239             # DO NOT SET THE NUMBER OF NON-ZEROS, THAT IS MANAGED BY THE set(...)
240             # function, which is called below
241              
242             # create a Math::SparseMatrix object
243 1         3 bless $self, $class;
244              
245             # also do error checks on file integrity
246 1         2 my $lineNum = 0;
247 1         3 my $nnzFound = 0;
248 1         5 while ($line = ) {
249 5         8 chomp $line;
250 5         7 $lineNum++;
251 5 50       11 if ($lineNum > $rows) {
252 0         0 die "Math::SparseMatrix->createTransposeFromFile error.\n".
253             "Number of columns in $infile is greater than that " .
254             "mentioned in the file header ($rows).";
255             }
256              
257 5         23 my @tokens = split / +/, $line;
258 5         8 my $index;
259             # process the pairs in each line
260 5         14 for ($index = 0; $index < @tokens; $index += 2) {
261             # the column number of the current pair is the row
262             # number in the transpose, the current line number is
263             # the column number in the transpose
264 5         18 $self->set($tokens[$index], $lineNum, $tokens[$index + 1]);
265 5         52 $nnzFound++;
266             }
267             }
268              
269 1 50       6 if ($nnzFound < $nnz) {
270 0         0 die "Math::SparseMatrix->createTransposeFromFile error.\n".
271             "Number of non-zero elements found in $infile is less than that " .
272             "mentioned in the file header -- $nnzFound < $nnz.\n";
273             }
274              
275 1 50       5 if ($nnzFound > $nnz) {
276 0         0 die "Math::SparseMatrix->createTransposeFromFile error.\n".
277             "Number of non-zero elements found in $infile is greater than that " .
278             "mentioned in the file header -- $nnzFound > $nnz.\n";
279             }
280              
281              
282             # now the transposed matrix is loaded and ready to be returned
283 1         4 return $self;
284             }
285              
286             sub writeToFile {
287              
288 1     1 0 7 my $self = shift;
289 1         3 my $outfile = shift;
290              
291 1 50       5 if (!defined $outfile) {
292 0         0 die "Math::SparseMatrix->writeToFile error.\n" .
293             "USAGE: \$spmatrix->writeToFile (\$output_file);\n";
294             }
295              
296             # write it to the output file
297 1 50       153 open(OUTPUT, "> $outfile") or
298             die "Math::SparseMatrix->writeToFile error.\n" .
299             "Failed to create output file $outfile.\n";
300 1         27 print OUTPUT $self->{_rows} . " " . $self->{_cols} . " " .
301             $self->{_nnz} . "\n";
302 1         3 my $row;
303 1         2 my $linecount = 1;
304 1         3 foreach $row (sort {$a <=> $b} keys %{$self->{_data}}) {
  6         27  
  1         14  
305 5 50       12 if ($row > $linecount) {
306             # add empty lines for empty rows
307 0         0 my $i = 0;
308 0         0 my $limit = $row - $linecount;
309 0         0 for ($i = 0; $i < $limit; $i++) {
310 0         0 print OUTPUT "\n";
311 0         0 $linecount++;
312             }
313             }
314 5         10 my $vec = $self->{_data}->{$row};
315 5         18 my $line = $vec->stringify();
316 5         136 print OUTPUT $line . "\n";
317 5         11 $linecount++;
318             }
319 1         69 close OUTPUT;
320             }
321              
322             sub printDims {
323 0     0 0   my $self = shift;
324 0           print "Rows: " . $self->{_rows} . ". Cols: " . $self->{_cols} . ".\n";
325             }
326              
327             1;
328             __END__