File Coverage

blib/lib/PDL/Matrix.pm
Criterion Covered Total %
statement 34 100 34.0
branch 8 32 25.0
condition 2 6 33.3
subroutine 12 28 42.8
pod 12 22 54.5
total 68 188 36.1


line stmt bran cond sub pod time code
1             =head1 NAME
2              
3             PDL::Matrix -- a convenience matrix class for column-major access
4              
5             =head1 VERSION
6              
7             This document refers to version PDL::Matrix 0.5 of PDL::Matrix
8              
9             =head1 SYNOPSIS
10              
11             use PDL::Matrix;
12              
13             $m = mpdl [[1,2,3],[4,5,6]];
14             $m = PDL::Matrix->pdl([[1,2,3],[4,5,6]]);
15             $m = msequence(4,3);
16             @dimsa = $x->mdims; # 'dims' is not overloaded
17              
18             $v = vpdl [0,1,2,3]
19             $v = vzeroes(4);
20              
21             =head1 DESCRIPTION
22              
23             =head2 Overview
24              
25             This package tries to help people who want to use PDL for 2D matrix
26             computation with lots of indexing involved. It provides a PDL
27             subclass so one- and two-dimensional ndarrays that are used as
28             vectors resp and matrices can be typed in using traditional matrix
29             convention.
30              
31             If you want to know more about matrix operation support in PDL, you
32             want to read L or L.
33              
34             The original pdl class refers to the first index as the first row,
35             the second index as the first column of a matrix. Consider
36              
37             print $B = sequence(3,2)
38             [
39             [0 1 2]
40             [3 4 5]
41             ]
42              
43             which gives a 2x3 matrix in terms of the matrix convention, but the
44             constructor used (3,2). This might get more confusing when using
45             slices like sequence(3,2)->slice("1:2,(0)") : with traditional
46             matrix convention one would expect [2 4] instead of [1 2].
47              
48             This subclass PDL::Matrix overloads the constructors and indexing
49             functions of pdls so that they are compatible with the usual matrix
50             convention, where the first dimension refers to the row of a
51             matrix. So now, the above example would be written as
52              
53             print $B = PDL::Matrix->sequence(3,2) # or $B = msequence(3,2)
54             [
55             [0 1]
56             [2 3]
57             [4 5]
58             ]
59              
60             Routines like L or
61             L can be used without any changes.
62              
63             Furthermore one can construct and use vectors as n x 1 matrices
64             without mentioning the second index '1'.
65              
66             =head2 Implementation
67              
68             C works by overloading a number of PDL constructors
69             and methods such that first and second args (corresponding to
70             first and second dims of corresponding matrices) are effectively swapped.
71             It is not yet clear if PDL::Matrix achieves a consistent column-major
72             look-and-feel in this way.
73              
74             =head1 NOTES
75              
76             As of version 0.5 (rewrite by CED) the matrices are stored in the usual
77             way, just constructed and stringified differently. That way indexing
78             and everything else works the way you think it should.
79              
80             =head1 FUNCTIONS
81              
82             =cut
83              
84             package PDL::Matrix;
85 1     1   983 use strict;
  1         2  
  1         32  
86 1     1   4 use warnings;
  1         1  
  1         36  
87              
88 1     1   2 use PDL::Exporter;
  1         3  
  1         5  
89 1     1   3 use Carp;
  1         2  
  1         173  
90              
91             our @EXPORT_OK;
92             our %EXPORT_TAGS = (Func=>\@EXPORT_OK);
93             our @ISA = qw/PDL::Exporter PDL/;
94             our $VERSION = "0.5";
95             $VERSION = eval $VERSION;
96              
97             #######################################################################=
98             #########
99             #
100             # overloads
101              
102             use overload( '""' => \&string,
103 2     2   26 'x' => sub {my $foo = $_[0]->null();
104 2         19 &PDL::Primitive::matmult(@_[1,0],$foo);
105 2         7 $foo;}
106 1     1   5 );
  1         2  
  1         12  
107              
108             sub string {
109 5     5 0 16 my ($me,@a) = shift;
110 5 50       39 return $me->SUPER::string(@a) unless($me->ndims > 0);
111 5 100       42 $me = $me->dummy(1,1) unless($me->ndims > 1);
112 5         32 $me->transpose->SUPER::string(@a);
113             }
114              
115              
116             # --------> constructors
117              
118             =head2 mpdl, PDL::Matrix::pdl
119              
120             =for ref
121              
122             constructs an object of class PDL::Matrix which is an ndarray child class.
123              
124             =for example
125              
126             $m = mpdl [[1,2,3],[4,5,6]];
127             $m = PDL::Matrix->pdl([[1,2,3],[4,5,6]]);
128              
129             =cut
130              
131             sub pdl {
132 1     1 1 4 my $class = shift;
133 1         20 my $pdl = $class->SUPER::pdl(@_)->transpose;
134 1   33     31 bless $pdl, ref $class || $class;
135             }
136              
137             =head2 mzeroes, mones, msequence
138              
139             =for ref
140              
141             constructs a PDL::Matrix object similar to the ndarray constructors
142             zeroes, ones, sequence.
143              
144             =cut
145              
146             for my $func (qw /pdl zeroes ones sequence dims/) {
147             push @EXPORT_OK, "m$func";
148 0     0 0 0 eval " sub m$func { PDL::Matrix->$func(\@_) }; ";
  0     0 1 0  
  1     1 1 192677  
  0     0 1 0  
  0     0 1 0  
149             }
150              
151             =head2 vpdl
152              
153             =for ref
154              
155             constructs an object of class PDL::Matrix which is of matrix
156             dimensions (n x 1)
157              
158             =for example
159              
160             print $v = vpdl [0,1];
161             [
162             [0]
163             [1]
164             ]
165              
166             =cut
167              
168             sub vpdl {
169 3     3 1 24 my $pdl = PDL->pdl(@_);
170 3         15 bless $pdl, __PACKAGE__;
171             }
172             push @EXPORT_OK, "vpdl";
173              
174             =head2 vzeroes, vones, vsequence
175              
176             =for ref
177              
178             constructs a PDL::Matrix object with matrix dimensions (n x 1),
179             therefore only the first scalar argument is used.
180              
181             =for example
182              
183             print $v = vsequence(2);
184             [
185             [0]
186             [1]
187             ]
188              
189             =cut
190              
191             for my $func (qw /zeroes ones sequence/) {
192             push @EXPORT_OK, "v$func";
193             my $code = << "EOE";
194              
195             sub v$func {
196             my \@arg = \@_;
197             ref(\$arg[0]) ne 'PDL::Type' ? (\@arg = (\$arg[0],1)) :
198             (\@arg = (\$arg[0],\$arg[1],1));
199             PDL::Matrix->$func(\@arg);
200             }
201              
202             EOE
203             # print "evaluating $code\n";
204 0 0   0 1 0 eval $code;
  0 0   0 1 0  
  0 0   0 1 0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
205             }
206              
207 1     1 0 437 sub inv { shift->transpose->SUPER::inv->transpose }
208              
209             =head2 kroneckerproduct
210              
211             =for ref
212              
213             returns kroneckerproduct of two matrices. This is not efficiently
214             implemented.
215              
216             =for example
217             print kroneckerproduct(msequence(2,2),mones(2,2))
218             [
219             [0 0 1 1]
220             [0 0 1 1]
221             [2 2 3 3]
222             [2 2 3 3]
223             ]
224              
225             =cut
226              
227             # returns kroneckerproduct of two matrices
228             sub kroneckerproduct {
229 0     0 1 0 my @arg = @_;
230            
231 0         0 my ($r0,$c0) = $arg[0]->mdims;
232 0         0 my ($r1,$c1) = $arg[1]->mdims;
233            
234 0         0 my $out = mzeroes($r0*$r1,$c0*$c1);
235            
236 0         0 for (my $i=0;$i<$r0;$i++) {
237 0         0 for (my $j=0;$j<$c0;$j++) {
238 0         0 ($_ = $out->slice(($i*$r1).":".(($i+1)*$r1-1).",".
239             ($j*$c1).":".(($j+1)*$c1-1)) ) .= $arg[0]->at($i,$j) * $arg[1];
240             }
241             }
242            
243 0         0 return $out;
244             }
245             push @EXPORT_OK, "kroneckerproduct";
246              
247             sub rotate {
248 0     0 0 0 my ($self,@args) = @_;
249 0         0 return $self->transpose->SUPER::rotate(@args)->transpose;
250             }
251              
252              
253             sub msumover {
254 0     0 0 0 my ($mpdl) = @_;
255 0         0 return PDL::sumover(transpose($mpdl)->xchg(0,2));
256             }
257             push @EXPORT_OK, "msumover";
258              
259              
260             =head2 det_general
261              
262             =for ref
263              
264             returns a generalized determinant of a matrix. If the matrix is not
265             regular, one can specify the rank of the matrix and the corresponding
266             subdeterminant is returned. This is implemented using the C
267             function.
268              
269             =for example
270             print msequence(3,3)->determinant(2) # determinant of
271             # regular 2x2 submatrix
272             -24
273              
274             =cut
275              
276             #
277             sub det_general {
278 0     0 1 0 my ($mpdl,$rank) = @_;
279 0         0 my $eigenvalues = (PDL::Math::eigens($mpdl))[1];
280 0         0 my @sort = list(PDL::Ufunc::qsorti(abs($eigenvalues)));
281 0         0 $eigenvalues = $eigenvalues->dice([@sort[-$rank..-1]]);
282 0         0 PDL::Ufunc::dprod($eigenvalues);
283             }
284              
285             =head2 trace
286              
287             =for ref
288              
289             returns the trace of a matrix (sum of diagonals)
290              
291             =cut
292              
293             sub trace {
294 0     0 1 0 my ($mpdl) = @_;
295 0         0 $mpdl->diagonal(0,1)->sum;
296             }
297              
298             # this has to be overloaded so that the PDL::slice
299             # is called and not PDL::Matrix::slice :-(
300             sub dummy($$;$) {
301 10     10 0 25 my ($pdl,$dim) = @_;
302 10 100       47 $dim = $pdl->getndims+1+$dim if $dim < 0;
303 10 50 33     65 barf ("too high/low dimension in call to dummy, allowed min/max=0/"
304             . $_[0]->getndims)
305             if $dim>$pdl->getndims || $dim < 0;
306 10 100       47 $_[2] = 1 if ($#_ < 2);
307 10         69 $pdl->PDL::slice((','x$dim)."*$_[2]");
308             }
309              
310              
311             # now some of my very own helper functions...
312             # stupid function to print a PDL::Matrix object in Maple code
313             sub stringifymaple {
314 0     0 0   my ($self,@args) = @_;
315              
316 0           my ($dimR,$dimC) = mdims($self);
317 0           my $s;
318              
319 0 0         $s .= $args[0].":=" unless $args[0] eq "";
320 0 0         if (defined($dimR)) {
321 0           $s .= "matrix($dimR,$dimC,[";
322 0           for(my $i=0;$i<$dimR;++$i) {
323 0           $s .= "[";
324 0           for(my $j=0;$j<$dimC;++$j) {
325 0           $s .= $self->at($i,$j);
326 0 0         $s .= "," if $j+1<$dimC;
327             }
328 0           $s .= "]";
329 0 0         $s .= "," if $i+1<$dimR;
330             }
331 0           $s .= "])";
332             }
333             else {
334 0           $s = "vector($dimC,[";
335 0           for(my $i=0;$i<$dimC;++$i) {
336 0           $s .= $self->at($i);
337 0 0         $s .= "," if $i+1<$dimC;
338             }
339 0           $s .= "])";
340             }
341 0           return $s;
342             }
343             sub printmaple {
344 0     0 0   print stringifymaple(@_).";\n";
345             }
346              
347             # stupid function to print a PDL::Matrix object in (La)TeX code
348             sub stringifyTeX {
349 0     0 0   my ($self,@args) = @_;
350              
351 0           my ($dimR,$dimC) = mdims($self);
352 0           my $s;
353              
354 0 0         $s .= $args[0]."=" unless $args[0] eq "";
355 0           $s .= "\\begin{pmatrix}\n";
356 0           for(my $i=0;$i<$dimR;++$i) {
357 0           for(my $j=0;$j<$dimC;++$j) {
358 0           $s .= $self->at($i,$j);
359 0 0         $s .= " & " if $j+1<$dimC;
360             }
361 0 0         $s .= " \\\\ \n" if $i+1<$dimR;
362             }
363 0           $s .= "\n \\end{pmatrix}\n";
364              
365 0           return $s;
366             }
367              
368             sub printTeX {
369 0     0 0   print stringifyTeX(@_)."\n";
370             }
371              
372             1;
373              
374             =head1 BUGS AND PROBLEMS
375              
376             Because we change the way ndarrays are constructed, not all pdl
377             operators may be applied to ndarray-matrices. The inner product is not
378             redefined. We might have missed some functions/methods. Internal
379             consistency of our approach needs yet to be established.
380              
381             Because PDL::Matrix changes the way slicing behaves, it breaks many
382             operators, notably those in MatrixOps.
383              
384             =head1 TODO
385              
386             check all PDL functions, benchmarks, optimization, lots of other things ...
387              
388             =head1 AUTHOR(S)
389              
390             Stephan Heuel (stephan@heuel.org), Christian Soeller
391             (c.soeller@auckland.ac.nz).
392              
393             =head1 COPYRIGHT
394              
395             All rights reserved. There is no warranty. You are allowed to
396             redistribute this software / documentation under certain
397             conditions. For details, see the file COPYING in the PDL
398             distribution. If this file is separated from the PDL distribution, the
399             copyright notice should be included in the file.
400              
401             =cut