File Coverage

blib/lib/PDL/CCS/Functions.pm
Criterion Covered Total %
statement 29 63 46.0
branch 4 32 12.5
condition 0 8 0.0
subroutine 9 12 75.0
pod 3 4 75.0
total 45 119 37.8


line stmt bran cond sub pod time code
1             ## File: PDL::CCS::Functions.pm
2             ## Author: Bryan Jurish
3             ## Description: useful perl-level functions for PDL::CCS
4              
5             package PDL::CCS::Functions;
6 2     2   13 use PDL::CCS::Config qw(ccs_indx);
  2         5  
  2         124  
7 2     2   1260 use PDL::CCS::Utils;
  2         50  
  2         47  
8 2     2   327 use PDL::VectorValued;
  2         3  
  2         12  
9 2     2   339 use PDL;
  2         3  
  2         30  
10 2     2   6653 use strict;
  2         3  
  2         1220  
11              
12             my @ccs_binops = qw(
13             plus minus mult divide modulo power
14             gt ge lt le eq ne spaceship
15             and2 or2 xor shiftleft shiftright
16             );
17              
18             our $VERSION = '1.24.1'; ##-- update with perl-reversion from Perl::Version module
19             our @ISA = ('PDL::Exporter');
20             our @EXPORT_OK =
21             (
22             ##
23             ##-- Decoding
24             qw(ccs_decode), #ccs_pointerlen
25             ##
26             ##-- Vector Operations (compat)
27             qw(ccs_binop_vector_mia),
28             (map "ccs_${_}_vector_mia", @ccs_binops),
29             ##
30             ##-- qsort
31             qw(ccs_qsort),
32             );
33              
34             our %EXPORT_TAGS =
35             (
36             Func => [@EXPORT_OK], ##-- respect PDL conventions (hopefully)
37             );
38              
39              
40             ##======================================================================
41             ## pod: headers
42             =pod
43              
44             =head1 NAME
45              
46             PDL::CCS::Functions - Useful perl-level functions for PDL::CCS
47              
48             =head1 SYNOPSIS
49              
50             use PDL;
51             use PDL::CCS::Functions;
52              
53             ##---------------------------------------------------------------------
54             ## ... stuff happens
55              
56             =cut
57              
58              
59             ##======================================================================
60             ## Decoding
61             =pod
62              
63             =head1 Decoding
64              
65             =cut
66              
67             ##-- DEPRECATED STEALTH METHOD: formerly a PDL::PP function in PDL::CCS::Utils
68             #*PDL::ccs_pointerlen = \&ccs_pointerlen;
69             sub ccs_pointerlen :lvalue {
70 0     0 0 0 my ($ptr,$len) = @_;
71 0 0       0 if (!defined($len)) {
72 0         0 $len = $ptr->slice("1:-1") - $ptr->slice("0:-2");
73             } else {
74 0         0 $len .= $ptr->slice("1:-1");
75 0         0 $len -= $ptr->slice("0:-2");
76             }
77 0         0 return $len;
78             }
79              
80              
81             ##---------------------------------------------------------------
82             ## Decoding: generic
83             =pod
84              
85             =head2 ccs_decode
86              
87             =for sig
88              
89             Signature: (indx whichnd(Ndims,Nnz); nzvals(Nnz); missing(); \@Dims; [o]a(@Dims))
90              
91             Decode a CCS-encoded matrix (no dataflow).
92              
93             =cut
94              
95             ;#-- emacs
96              
97             *PDL::ccs_decode = \&ccs_decode;
98             sub ccs_decode :lvalue {
99 7     7 1 19 my ($aw,$nzvals,$missing,$dims,$a) = @_;
100 7 50       18 $missing = $PDL::undefval if (!defined($missing));
101 7 50       14 if (!defined($dims)) {
102 0 0       0 barf("PDL::CCS::ccs_decode(): whichnd() is empty; you must specify \@Dims!") if ($aw->isempty);
103 0         0 $dims = [ map {$aw->slice("($_),")->max+1} (0..($aw->dim(0)-1))];
  0         0  
104             }
105 7 100       19 $a = zeroes($nzvals->type, @$dims) if (!defined($a));
106 7         55 $a .= $missing;
107              
108 7         112 (my $tmp=$a->indexND($aw)) .= $nzvals; ##-- CPAN tests puke here with "Can't modify non-lvalue subroutine call" in 5.15.x (perl bug #107366)
109              
110             ##-- workaround for missing empty pdl support in PDL 2.4.10 release candidates (pdl bug #3462924), fixed in 2.4.9_993
111             #$a->indexND($aw) .= $nzvals if (!$nzvals->isempty);
112             #if (!$nzvals->isempty) {
113             # my $tmp = $a->indexND($aw);
114             # $tmp .= $nzvals;
115             #}
116              
117 7         450 return $a;
118             }
119              
120             ##======================================================================
121             ## Scalar Operations
122             =pod
123              
124             =head1 Scalar Operations
125              
126             Scalar operations can be performed in parallel directly on C<$nzvals>
127             (and if applicable on C<$missing> as well):
128              
129             $c = 42;
130              
131             $nzvals2 = $nzvals + $c; $missing2 = $missing + $c;
132             $nzvals2 = $nzvals - $c; $missing2 = $missing - $c;
133             $nzvals2 = $nzvals * $c; $missing2 = $missing * $c;
134             $nzvals2 = $nzvals / $c; $missing2 = $missing / $c;
135              
136             $nzvals2 = $nzvals ** $c; $missing2 = $missing ** $c;
137             $nzvals2 = log($nzvals); $missing2 = log($missing);
138             $nzvals2 = exp($nzvals); $missing2 = exp(missing);
139              
140             $nzvals2 = $nzvals->and2($c,0); $missing2 = $missing->and($c,0);
141             $nzvals2 = $nzvals->or2($c,0); $missing2 = $missing->or2($c,0);
142             $nzvals2 = $nzvals->not(); $missing2 = $missing->not();
143              
144             Nothing prevents scalar operations from producing new "missing" values (e.g. $nzvals*0),
145             so you might want to re-encode your compressed data after applying the operation.
146              
147             =cut
148              
149              
150             ##======================================================================
151             ## Vector Operations
152             =pod
153              
154             =head1 Vector Operations
155              
156             =head2 ccs_OP_vector_mia
157              
158             =for sig
159              
160             Signature: (indx whichDimV(Nnz); nzvals(Nnz); vec(V); [o]nzvals_out(Nnz))
161              
162             A number of row- and column-vector operations may be performed directly
163             on encoded Nd-PDLs, without the need for decoding to a (potentially huge)
164             dense temporary. These operations assume that "missing" values are
165             annihilators with respect to the operation in question, i.e.
166             that it holds for all C<$x> in C<$vec> that:
167              
168             ($missing __OP__ $x) == $missing
169              
170             This is in line with the usual PDL semantics if your C<$missing> value is C,
171             but may produce unexpected results when e.g. adding a vector to a sparse PDL with C<$missing>==0.
172             If you really need to do something like the latter, then you're probably better off decoding to
173             a dense PDL anyway.
174              
175             Predefined function names for encoded-PDL vector operations are all of the form:
176             C, where ${OPNAME} is the base name of the operation:
177              
178             plus ##-- addition
179             minus ##-- subtraction
180             mult ##-- multiplication (NOT matrix-multiplication)
181             divide ##-- division
182             modulo ##-- modulo
183             power ##-- potentiation
184              
185             gt ##-- greater-than
186             ge ##-- greater-than-or-equal
187             lt ##-- less-than
188             le ##-- less-than-or-equal
189             eq ##-- equality
190             ne ##-- inequality
191             spaceship ##-- 3-way comparison
192              
193             and2 ##-- binary AND
194             or2 ##-- binary OR
195             xor ##-- binary XOR
196             shiftleft ##-- left-shift
197             shiftright ##-- right-shift
198              
199             =head2 \&CODE = ccs_binop_vector_mia($opName, \&PDLCODE);
200              
201             Returns a generic vector-operation subroutine which reports errors as C<$opName>
202             and uses \&PDLCODE to perform underlying computation.
203              
204             =cut
205              
206             ##======================================================================
207             ## Vector Operations: Generic
208              
209             *PDL::ccs_binop_vector_mia = \&ccs_binop_vector_mia;
210             sub ccs_binop_vector_mia {
211 36     36 1 58 my ($opName,$pdlCode) = @_;
212             return sub :lvalue {
213 2     2   5 my ($wi, $nzvals_in, $vec) = @_;
214 2         50 my $tmp = $pdlCode->($nzvals_in, $vec->index($wi), 0); # $tmp for perl -d
215 36         128 };
216             }
217              
218             for (@ccs_binops) {
219 2     2   16 no strict 'refs';
  2         3  
  2         1367  
220             *{"PDL::ccs_${_}_vector_mia"} = *{"ccs_${_}_vector_mia"} = ccs_binop_vector_mia($_, PDL->can($_));
221             }
222              
223             ##======================================================================
224             ## Sorting
225             =pod
226              
227             =head1 Sorting
228              
229             =head2 ccs_qsort
230              
231             =for sig
232              
233             Signature: (indx which(Ndims,Nnz); nzvals(Nnz); missing(); Dim0(); indx [o]nzix(Nnz); indx [o]nzenum(Nnz))
234              
235             Underlying guts for PDL::CCS::Nd::qsort() and PDL::CCS::Nd::qsorti().
236             Given a set of $Nnz items $i each associated with a vector-key C<$which(:,$i)>
237             and a value C<$nzvals($i)>, returns a vector of $Nnz item indices C<$nzix()>
238             such that C<$which(:,$nzix)> is vector-sorted in ascending order and
239             C<$nzvals(:,$nzix)> are sorted in ascending order for each unique key-vector in
240             C<$which()>, and an enumeration C<$nzenum()> of items for each unique key-vector
241             in terms of the sorted data: C<$nzenum($j)> is the logical position of the item
242             C<$nzix($j)>.
243              
244             If C<$missing> and C<$Dim0> are defined,
245             items C<$i=$nzix($j)> with values C<$nzvals($i) E $missing>
246             will be logically enumerated at the end of the range [0,$Dim0-1]
247             and there will be a gap between C<$nzenum()> values for a C<$which()>-key
248             with fewer than $Dim0 instances; otherwise $nzenum() values will be
249             enumerated in ascending order starting from 0.
250              
251             For an unsorted index+value dataset C<($which0,$nzvals0)> with
252              
253             ($nzix,$nzenum) = ccs_qsort($which0("1:-1,"),$nzvals0,$missing,$which0("0,")->max+1)
254              
255             qsort() can be implemented as:
256              
257             $which = $nzenum("*1,")->glue(0,$which0("1:-1,")->dice_axis(1,$nzix));
258             $nzvals = $nzvals0->index($nzix);
259              
260             and qsorti() as:
261              
262             $which = $nzenum("*1,")->glue(0,$which0("1:-1,")->dice_axis(1,$nzix));
263             $nzvals = $which0("(0),")->index($nzix);
264              
265             =cut
266              
267             ## $bool = _checkdims(\@dims1,\@dims2,$label); ##-- match @dims1 ~ @dims2
268             ## $bool = _checkdims( $pdl1, $pdl2,$label); ##-- match $pdl1->dims ~ $pdl2->dims
269             sub _checkdims {
270             #my ($dims1,$dims2,$label) = @_;
271             #my ($pdl1,$pdl2,$label) = @_;
272 0 0   0     my $d0 = UNIVERSAL::isa($_[0],'PDL') ? pdl(ccs_indx(),$_[0]->dims) : pdl(ccs_indx(),$_[0]);
273 0 0         my $d1 = UNIVERSAL::isa($_[1],'PDL') ? pdl(ccs_indx(),$_[1]->dims) : pdl(ccs_indx(),$_[0]);
274 0 0 0       barf(__PACKAGE__ . "::_checkdims(): dimension mismatch for ".($_[2]||'pdl').": $d0!=$d1")
      0        
275             if (($d0->nelem!=$d1->nelem) || !all($d0==$d1));
276 0           return 1;
277             }
278              
279             sub ccs_qsort {
280 0     0 1   my ($which,$nzvals, $missing,$dim0, $nzix,$nzenum) = @_;
281              
282             ##-- prepare: $nzix
283 0 0         $nzix = zeroes(ccs_indx(),$nzvals->dims) if (!defined($nzix));
284 0 0         $nzix->reshape($nzvals) if ($nzix->isempty);
285 0           _checkdims($nzvals,$nzix,'ccs_qsort: nzvals~nzix');
286             ##
287             ##-- prepare: $nzenum
288 0 0         $nzenum = zeroes(ccs_indx(),$nzvals->dims) if (!defined($nzenum));
289 0 0         $nzenum->reshape($nzvals) if ($nzenum->isempty);
290 0           _checkdims($nzenum,$nzvals,'ccs_qsort: nzvals~nzenum');
291              
292             ##-- collect and sort base data (unsorted indices + values)
293 0           my $vdata = $which->glue(0,$nzvals->slice("*1,"));
294 0           $vdata->vv_qsortveci($nzix);
295              
296             ##-- get logical enumeration
297 0 0 0       if (!defined($missing) || !defined($dim0)) {
298             ##-- ... flat enumeration
299 0           $which->dice_axis(1,$nzix)->enumvec($nzenum);
300             } else {
301             ##-- ... we have $missing and $dim0: split enumeration around $missing()
302 0           my $whichx = $which->dice_axis(1,$nzix);
303 0           my $nzvalsx = $nzvals->index($nzix);
304 0           my ($nzii_l,$nzii_r) = which_both($nzvalsx <= $missing);
305             #$nzenum .= -1; ##-- debug
306 0 0         $whichx->dice_axis(1,$nzii_l)->enumvec($nzenum->index($nzii_l)) if (!$nzii_l->isempty); ##-- enum: <=$missing
307 0 0         if (!$nzii_r->isempty) {
308             ##-- enum: >$missing
309 0           my $nzenum_r = $nzenum->index($nzii_r);
310 0           $whichx->dice_axis(1,$nzii_r)->slice(",-1:0")->enumvec($nzenum_r->slice("-1:0"));
311 0           $nzenum_r *= -1;
312 0           $nzenum_r += ($dim0-1);
313             }
314             }
315              
316             ##-- all done
317 0 0         return wantarray ? ($nzix,$nzenum) : $nzix;
318             }
319              
320              
321             ##======================================================================
322             ## Vector Operations: Generic
323              
324              
325             ##======================================================================
326             ## POD: footer
327             =pod
328              
329             =head1 ACKNOWLEDGEMENTS
330              
331             Perl by Larry Wall.
332              
333             PDL by Karl Glazebrook, Tuomas J. Lukka, Christian Soeller, and others.
334              
335             =cut
336              
337              
338             ##---------------------------------------------------------------------
339             =pod
340              
341             =head1 AUTHOR
342              
343             Bryan Jurish Emoocow@cpan.orgE
344              
345             =head2 Copyright Policy
346              
347             Copyright (C) 2007-2024, Bryan Jurish. All rights reserved.
348              
349             This package is free software, and entirely without warranty.
350             You may redistribute it and/or modify it under the same terms
351             as Perl itself.
352              
353             =head1 SEE ALSO
354              
355             perl(1),
356             PDL(3perl),
357             PDL::CCS::Nd(3perl),
358              
359              
360             =cut
361              
362              
363             1; ##-- make perl happy