Nd(3pm)
β¨ NAME β¨
okay bestie, welcome to PDL::CCS::Nd(3pm) β the manual page for the N-dimensional sparse pseudo-PDL glow-up. π¦
PDL::CCS::Nd β N-dimensional sparse pseudo-PDLs π
πΈ SYNOPSIS πΈ
bestie, this synopsis is long, but it's giving organized. every constructor, every ufunc, every low-level accessor β all here. no cap.
use PDL;
use PDL::CCS::Nd;
##---------------------------------------------------------------------
## Example data
$missing = 0; ##-- missing values
$dense = random(@dims); ##-- densely encoded pdl
$dense->where(random(@dims)<=0.95) .= $missing; ## ... made sparse
$whichND = $dense->whichND; ##-- which values are present?
$nzvals = $dense->indexND($whichND); ##-- ... and what are they?
##---------------------------------------------------------------------
## Constructors etc.
$ccs = PDL::CCS:Nd->newFromDense($dense,%args); ##-- construct from dense matrix
$ccs = PDL::CCS:Nd->newFromWhich($whichND,$nzvals,%args); ##-- construct from index+value pairs
$ccs = $dense->toccs(); ##-- ensure PDL::CCS::Nd-hood
$ccs = $ccs->toccs(); ##-- ... analogous to PDL::topdl()
$ccs = $dense->toccs($missing,$flags); ##-- ... with optional arguments
$ccs2 = $ccs->copy(); ##-- copy constructor
$ccs2 = $ccs->copyShallow(); ##-- shallow copy, mainly for internal use
$ccs2 = $ccs->shadow(%args); ##-- flexible copy method, for internal use
##---------------------------------------------------------------------
## Maintenance & Decoding
$ccs = $ccs->recode(); ##-- remove missing values from stored VALS
$ccs = $ccs->sortwhich(); ##-- internal use only
$dense2 = $ccs->decode(); ##-- extract to a (new) dense matrix
$dense2 = $ccs->todense(); ##-- ensure dense storage
$dense2 = $dense2->todense(); ##-- ... analogous to PDL::topdl()
##---------------------------------------------------------------------
## PDL API: Basic Properties
##---------------------------------------
## Type conversion & Checking
$ccs2 = $ccs->convert($type);
$ccs2 = $ccs->byte;
$ccs2 = $ccs->short;
$ccs2 = $ccs->ushort;
$ccs2 = $ccs->long;
$ccs2 = $ccs->longlong;
$ccs2 = $ccs->float;
$ccs2 = $ccs->double;
##---------------------------------------
## Dimensions
@dims = $ccs->dims();
$ndims = $ccs->ndims();
$dim = $ccs->dim($dimi);
$nelem = $ccs->nelem;
$bool = $ccs->isnull;
$bool = $ccs->isempty;
##---------------------------------------
## Inplace & Dataflow
$ccs = $ccs->inplace();
$bool = $ccs->is_inplace;
$bool = $ccs->set_inplace($bool);
$ccs = $ccs->sever;
##---------------------------------------
## Bad Value Handling
$bool = $ccs->bad_is_missing(); ##-- treat BAD values as missing?
$bool = $ccs->bad_is_missing($bool);
$ccs = $ccs->badmissing(); ##-- ... a la inplace()
$bool = $ccs->nan_is_missing(); ##-- treat NaN values as missing?
$bool = $ccs->nan_is_missing($bool);
$ccs = $ccs->nanmissing(); ##-- ... a la inplace()
$ccs2 = $ccs->setnantobad();
$ccs2 = $ccs->setbadtonan();
$ccs2 = $ccs->setbadtoval($val);
$ccs2 = $ccs->setvaltobad($val);
##---------------------------------------------------------------------
## PDL API: Dimension Shuffling
$ccs2 = $ccs->dummy($vdimi,$size);
$ccs2 = $ccs->reorder(@vdims);
$ccs2 = $ccs->xchg($vdim1,$vdim2);
$ccs2 = $ccs->mv($vdimFrom,$vdimTo);
$ccs2 = $ccs->transpose();
##---------------------------------------------------------------------
## PDL API: Indexing
$nzi = $ccs->indexNDi($ndi); ##-- guts for indexing methods
$ndi = $ccs->n2oned($ndi); ##-- returns 1d pseudo-index for $ccs
$ivals = $ccs->indexND($ndi);
$ivals = $ccs->index2d($xi,$yi);
$ivals = $ccs->index($flati); ##-- buggy: no pseudo-threading!
$ccs2 = $ccs->dice_axis($vaxis,$vaxis_ix);
$nzi = $ccs->xindex1d($xi); ##-- nz-indices along 0th dimension
$nzi = $ccs->pxindex1d($dimi,$xi); ##-- ... or any dimension, using ptr()
$nzi = $ccs->xindex2d($xi,$yi); ##-- ... or for Cartesian product on 2d matrix
$ccs2 = $ccs->xsubset1d($xi); ##-- subset along 0th dimension
$ccs2 = $ccs->pxsubset1d($dimi,$xi); ##-- ... or any dimension, using ptr()
$ccs2 = $ccs->xsubset2d($xi,$yi); ##-- ... or for Cartesian product on 2d matrix
$whichND = $ccs->whichND();
$vals = $ccs->whichVals(); ##-- like $ccs->indexND($ccs->whichND), but faster
$which = $ccs->which()
$value = $ccs->at(@index);
$ccs = $ccs->set(@index,$value);
##---------------------------------------------------------------------
## PDL API: Ufuncs
$ccs2 = $ccs->prodover;
$ccs2 = $ccs->dprodover;
$ccs2 = $ccs->sumover;
$ccs2 = $ccs->dsumover;
$ccs2 = $ccs->andover;
$ccs2 = $ccs->orover;
$ccs2 = $ccs->bandover;
$ccs2 = $ccs->borover;
$ccs2 = $ccs->maximum;
$ccs2 = $ccs->minimum;
$ccs2 = $ccs->maximum_ind; ##-- -1 indicates "missing" value is maximal
$ccs2 = $ccs->minimum_ind; ##-- -1 indicates "missing" value is minimal
$ccs2 = $ccs->nbadover;
$ccs2 = $ccs->ngoodover;
$ccs2 = $ccs->nnz;
$sclr = $ccs->prod;
$sclr = $ccs->dprod;
$sclr = $ccs->sum;
$sclr = $ccs->dsum;
$sclr = $ccs->nbad;
$sclr = $ccs->ngood;
$sclr = $ccs->min;
$sclr = $ccs->max;
$bool = $ccs->any;
$bool = $ccs->all;
##---------------------------------------------------------------------
## PDL API: Unary Operations (Overloaded)
$ccs2 = $ccs->bitnot; $ccs2 = ~$ccs;
$ccs2 = $ccs->not; $ccs2 = !$ccs;
$ccs2 = $ccs->sqrt;
$ccs2 = $ccs->abs;
$ccs2 = $ccs->sin;
$ccs2 = $ccs->cos;
$ccs2 = $ccs->exp;
$ccs2 = $ccs->log;
$ccs2 = $ccs->log10;
##---------------------------------------------------------------------
## PDL API: Binary Operations (missing is annihilator)
## + $b may be a perl scalar, a dense PDL, or a PDL::CCS::Nd object
## + $c is always returned as a PDL::CCS::Nd ojbect
##---------------------------------------
## Arithmetic
$c = $ccs->plus($b); $c = $ccs1 + $b;
$c = $ccs->minus($b); $c = $ccs1 - $b;
$c = $ccs->mult($b); $c = $ccs1 * $b;
$c = $ccs->divide($b); $c = $ccs1 / $b;
$c = $ccs->modulo($b); $c = $ccs1 % $b;
$c = $ccs->power($b); $c = $ccs1 ** $b;
##---------------------------------------
## Comparisons
$c = $ccs->gt($b); $c = ($ccs > $b);
$c = $ccs->ge($b); $c = ($ccs >= $b);
$c = $ccs->lt($b); $c = ($ccs < $b);
$c = $ccs->le($b); $c = ($ccs <= $b);
$c = $ccs->eq($b); $c = ($ccs == $b);
$c = $ccs->ne($b); $c = ($ccs != $b);
$c = $ccs->spaceship($b); $c = ($ccs <=> $b);
##---------------------------------------
## Bitwise Operations
$c = $ccs->and2($b); $c = ($ccs & $b);
$c = $ccs->or2($b); $c = ($ccs | $b);
$c = $ccs->xor($b); $c = ($ccs ^ $b);
$c = $ccs->shiftleft($b); $c = ($ccs << $b);
$c = $ccs->shiftright($b); $c = ($ccs >> $b);
##---------------------------------------
## Matrix Operations
$c = $ccs->inner($b);
$c = $ccs->matmult($b); $c = $ccs x $b;
$c_dense = $ccs->matmult2d_sdd($b_dense, $zc);
$c_dense = $ccs->matmult2d_zdd($b_dense);
$vnorm = $ccs->vnorm($pdimi);
$vcos = $ccs->vcos_zdd($b_dense);
$vcos = $ccs->vcos_pzd($b_ccs);
##---------------------------------------
## Other Operations
$ccs->rassgn($b); $ccs .= $b;
$str = $ccs->string(); $str = "$ccs";
##---------------------------------------------------------------------
## Indexing Utilities
##---------------------------------------------------------------------
## Low-Level Object Access
$num_v_per_p = $ccs->_ccs_nvperp; ##-- num virtual / num physical
$pdims = $ccs->pdims; $vdims = $ccs->vdims; ##-- physical|virtual dim pdl
$nelem = $ccs->nelem_p; $nelem = $ccs->nelem_v; ##-- physical|virtual nelem
$nstored = $ccs->nstored_p; $nstored = $ccs->nstored_v; ##-- physical|virtual Nnz+1
$nmissing = $ccs->nmissing_p; $nmissing = $ccs->nmissing_v; ##-- physical|virtual nelem-Nnz
$ccs = $ccs->make_physically_indexed(); ##-- ensure all dimensions are physically indexed
$bool = $ccs->allmissing(); ##-- are all values missing?
$missing_val = $ccs->missing; ##-- get missing value
$missing_val = $ccs->missing($missing_val); ##-- set missing value
$ccs = $ccs->_missing($missing_val); ##-- ... returning the object
$whichND_phys = $ccs->_whichND(); ##-- get/set physical indices
$whichND_phys = $ccs->_whichND($whichND_phys);
$nzvals_phys = $ccs->_nzvals(); ##-- get/set physically indexed values
$nzvals_phys = $ccs->_nzvals($vals_phys);
$vals_phys = $ccs->_vals(); ##-- get/set physically indexed values
$vals_phys = $ccs->_vals($vals_phys);
$bool = $ccs->hasptr($pdimi); ##-- check for cached Harwell-Boeing pointer
($ptr,$ptrix) = $ccs->ptr($pdimi); ##-- ... get one, caching for later use
($ptr,$ptrix) = $ccs->getptr($pdimi); ##-- ... compute one, regardless of cache
($ptr,$ptrix) = $ccs->setptr($pdimi,$p,$pix); ##-- ... set a cached pointer
$ccs->clearptr($pdimi); ##-- ... clear a cached pointer
$ccs->clearptrs(); ##-- ... clear all cached pointers
$flags = $ccs->flags(); ##-- get/set object-local flags
$flags = $ccs->flags($flags);
$density = $ccs->density; ##-- get object density
$crate = $ccs->compressionRate; ##-- get compression rate
π DESCRIPTION π
okay so PDL::CCS::Nd is the object-oriented bestie for sparse N-dimensional vectors & matrices. it uses a whole set of low-level PDLs to encode the non-missing values. right now, only a portion of the PDL API is implemented β so it's not the entire PDL cinematic universe yet, but it's still iconic. π
more sections are loading in the next parts, bestie. don't go anywhere. π¦β¨
π GLOBALS π
okay bestie, so like, the following package-global variables are defined? periodt. let's get into it. π
π’ Block Size Constants π’
$BINOP_BLOCKSIZE_MIN = 1;$BINOP_BLOCKSIZE_MAX = 0;
These are the minimum (and maximum) block size for block-wise incremental computation of binary operations. A value of zero or undef means there's no minimum (or no maximum). lowkey important.
𧬠Object Structure π§¬
PDL::CCS::Nd objects are implemented as perl ARRAY-references. So for more intuitive access to object components, you can use the following package-global variables as array indices to access the internal object structure: no cap.
$PDIMS
Indexes a pdl(long,$NPdims) of physically indexed dimension sizes:
$ccs->[$PDIMS]->at($pdim_i) == $dimSize_i
$VDIMS
Indexes a pdl(long,$NVdims) of "virtual" dimension sizes:
$ccs->[$VDIMS]->at($vdim_i) == / -$vdimSize_i if $vdim_i is a dummy dimension
\ $pdim_i otherwise
The $VDIMS piddle is used for dimension-shuffling transformations such as xchg() and reorder(), as well as for dummy(). slay.
$WHICH
Indexes a pdl(long,$NPdims,$Nnz) of the "physical indices" of all non-missing values in the non-dummy dimensions of the corresponding dense matrix. Vectors in $WHICH are guaranteed to be sorted in lexicographic order. If your $missing value is zero, and if your qsortvec() function works, it should be the case that:
all( $ccs->[$WHICH] == $dense->whichND->qsortvec )
A "physically indexed dimension" is just a dimension corresponding to a single column of the $WHICH pdl, whereas a dummy dimension does not correspond to any physically indexed dimension.
$VALS
Indexes a vector pdl($valType, $Nnz+1) of all values in the sparse matrix, where $Nnz is the number of non-missing values in the sparse matrix. Non-final elements of the $VALS piddle are interpreted as the values of the corresponding indices in the $WHICH piddle:
all( $ccs->[$VALS]->slice("0:-2") == $dense->indexND($ccs->[$WHICH]) )
The final element of the $VALS piddle is referred to as "$missing", and represents the value of all elements of the dense physical matrix whose indices are not explicitly listed in the $WHICH piddle:
all( $ccs->[$VALS]->slice("-1") == $dense->flat->index(which(!$dense)) )
$PTRS
Indexes an array of arrays containing Harwell-Boeing "pointer" piddle pairs for the corresponding physically indexed dimension. For a physically indexed dimension $d of size $N, $ccs->[$PTRS][$d] (if it exists) is a pair [$ptr,$ptrix] as returned by PDL::CCS::Utils::ccs_encode_pointers($WHICH,$N), which are such that:
$ptrβ$ptris apdl(long,$N+1)containing the offsets in$ptrixcorresponding to the first non-missing value in the dimension$d. For all$i,0 <= $i < $N,$ptr($i)contains the index of the first non-missing value (if any) from column$iof$dense(...,N,...)encoded in the$WHICHpiddle.$ptr($N+1)contains the number of physically indexed cells in the$WHICHpiddle.$ptrixβ Is an index piddle intodim(1)of$WHICHrsp.dim(0)of$VALSwhose key positions correspond to the offsets listed in$ptr. The point here is that:
$WHICH->dice_axis(1,$ptrix)
is guaranteed to be primarily sorted along the pointer dimension $d, and stably sorted along all other dimensions, e.g. should be identical to:
$WHICH->mv($d,0)->qsortvec->mv(0,$d)
$FLAGS
Indexes a perl scalar containing some object-local flags. See "Object Flags" for details.
$USER
Indexes the first unused position in the object array. If you derive a class from PDL::CCS::Nd, you should use this position to place any new object-local data. π
π© Object Flags π©
The following object-local constants are defined as bitmask flags:
$CCSND_BAD_IS_MISSINGβ Bitmask of the "bad-is-missing" flag. See thebad_is_missing()method.$CCSND_NAN_IS_MISSINGβ Bitmask of the "NaN-is-missing" flag. See thenan_is_missing()method.$CCSND_INPLACEβ Bitmask of the "inplace" flag. SeePDL::Corefor details.$CCSND_FLAGS_DEFAULTβ Default flags for new objects.
π METHODS π
ποΈ Constructors, etc. ποΈ
$class_or_obj->newFromDense($dense,$missing,$flags)
Signature ($class_or_obj; dense(N1,...,NNdims); missing(); int flags)
Class method. Create and return a new PDL::CCS::Nd object from a dense N-dimensional PDL $dense. If specified, $missing is used as the value for "missing" elements, and $flags are used to initialize the object-local flags. $missing defaults to BAD if the bad flag of $dense is set, otherwise $missing defaults to zero. no cap.
$ccs->fromDense($dense,$missing,$flags)
Signature ($ccs; dense(N1,...,NNdims); missing(); int flags)
Object method. Populate a sparse matrix object from a dense piddle $dense. See newFromDense(). π
$class_or_obj->newFromWhich($whichND,$nzvals,%options)
Signature ($class_or_obj; int whichND(Ndims,Nnz); nzvals(Nnz+1); %options)
Class method. Create and return a new PDL::CCS::Nd object from a set of indices $whichND of non-missing elements in a (hypothetical) dense piddle and a vector $nzvals of the corresponding values. Known %options:
sorted => $bool, ##-- if true, $whichND is assumed to be pre-sorted
steal => $bool, ##-- if true, $whichND and $nzvals are used literally (formerly implied 'sorted')
## + in this case, $nzvals should really be: $nzvals->append($missing)
pdims => $pdims, ##-- physical dimension list; default guessed from $whichND (alias: 'dims')
vdims => $vdims, ##-- virtual dims (default: sequence($nPhysDims)); alias: 'xdims'
missing => $missing, ##-- default: BAD if $nzvals->badflag, 0 otherwise
flags => $flags ##-- flags
$ccs->fromWhich($whichND,$nzvals,%options)
Object method. Guts for newFromWhich().
$a->toccs($missing,$flags)
Wrapper for newFromDense(). Return a PDL::CCS::Nd object for any piddle or perl scalar $a. If $a is already a PDL::CCS::Nd object, just returns $a. This method gets exported into the PDL namespace for ease of use. periodt.
$ccs = $ccs->copy()
Full copy constructor.
$ccs2 = $ccs->copyShallow()
Shallow copy constructor, used e.g. by dimension-shuffling transformations. Copied components:
$PDIMS, @$PTRS, @{$PTRS->[*]}, $FLAGS
Referenced components:
$VDIMS, $WHICH, $VALS, $PTRS->[*][*]
$ccs2 = $ccs1->shadow(%args)
Flexible constructor for computed PDL::CCS::Nd objects. Known %args:
to => $ccs2, ##-- default: new
pdims => $pdims2, ##-- default: $pdims1->pdl (alias: 'dims')
vdims => $vdims2, ##-- default: $vdims1->pdl (alias: 'xdims')
ptrs => \@ptrs2, ##-- default: []
which => $which2, ##-- default: undef
vals => $vals2, ##-- default: undef
flags => $flags, ##-- default: $flags1
π οΈ Maintenance & Decoding π οΈ
$ccs = $ccs->recode()
Recodes the PDL::CCS::Nd object, removing any missing values from its $VALS piddle.
$ccs = $ccs->sortwhich()
Lexicographically sorts $ccs->[$WHICH], altering $VALS accordingly. Clears $PTRS.
$dense = $ccs->decode() / $dense = $ccs->decode($dense)
Decode a PDL::CCS::Nd object to a dense piddle. Dummy dimensions in $ccs should be created as dummy dimensions in $dense.
$dense = $a->todense()
Ensures that $a is not a PDL::CCS::Nd by wrapping decode(). For PDLs or perl scalars, just returns $a.
π§° PDL API: Basic Properties π§°
The following basic PDL API methods are implemented and/or wrapped for PDL::CCS::Nd objects:
Type Checking & Conversion
type, convert, byte, short, ushort, long, double Type-checking and conversion routines are passed on to the $VALS sub-piddle.
Dimension Access
dims, dim, getdim, ndims, getndims, nelem, isnull, isempty Note that nelem() returns the number of hypothetically addressable cells -- the number of cells in the corresponding dense matrix, rather than the number of non-missing elements actually stored. π
Inplace Operations
set_inplace($bool), is_inplace(), inplace()
Dataflow
sever
Bad Value Handling
setnantobad, setbadtonan, setbadtoval, setvaltobad See also the bad_is_missing() and nan_is_missing() methods, below.
π PDL API: Dimension Shuffling π
The following dimension-shuffling methods are supported, and should be compatible to their PDL counterparts:
dummy($vdimi) / dummy($vdimi, $size)
Insert a "virtual" dummy dimension of size $size at dimension index $vdimi.
reorder(@vdim_list)
Reorder dimensions according to @vdim_list.
xchg($vdim1,$vdim2)
Exchange two dimensions.
mv($vdimFrom, $vdimTo)
Move a dimension to another position, shoving remaining dimensions out of the way to make room.
transpose()
Always copies, unlike xchg(). Also unlike xchg(), works for 1d row-vectors. π
π PDL API: Indexing π
indexNDi($ndi)
Signature: ($ccs; int ndi(NVdims,Nind); int [o]nzi(Nind))
Guts for indexing methods. Given an N-dimensional index piddle $ndi, return a 1d index vector into $VALS for the corresponding values. Missing values are returned in $nzi as $Nnz == $ccs->_nnz_p; Uses PDL::VectorValues::vsearchvec() internally, so expect O(Ndims * log(Nnz)) complexity. Although the theoretical complexity is tough to beat, this method could be made much faster in the usual (read "sparse") case by an intelligent use of $PTRS if and when available. π₯
indexND($ndi) / index2d($xi,$yi)
Should be mostly compatible to the PDL functions of the same names, but without any boundary handling.
index($flati)
Implicitly flattens the source pdl. This ought to be fixed. π
dice_axis($axis_v, $axisi)
Should be compatible with the PDL function of the same name. Returns a new PDL::CCS::Nd object which should participate in dataflow.
xindex1d($xi)
Get non-missing indices for any element of $xi along 0th dimension; $xi must be sorted in ascending order.
pxindex1d($dimi,$xi)
Get non-missing indices for any element of $xi along physically indexed dimension $dimi, using "ptr" in ptr($dimi). $xi must be sorted in ascending order.
xindex2d($xi,$yi)
Get non-missing indices for any element in Cartesian product ($xi x $yi) for 2d sparse matrix. $xi and $yi must be sorted in ascending order.
xsubset1d($xi)β hands you a subset object, basically likedice_axis(0,$x)but with no renumbering of the indices along the diced dimension. tiny catch:$xihas to be sorted in ascending order. πpxsubset1d($dimi,$xi)β same energy asdice_axis($dimi,$x), subset object, and again no renumbering along the diced dimension. and again,$ximust be sorted ascending. no exceptions bestie. πxsubset2d($xi,$yi)β returns a subset object similar toindexND( $xi->slice("*1,")->cat($yi)->clump(2)->xchg(0,1) ), but without renumbering indices. both$xiand$yimust be sorted in ascending order. yes, both. πΈn2oned($ndi)β returns a 1d pseudo-index, used internally for implementingwhich(), etc. backend stuff, iykyk.whichND()β should behave mostly like the PDL function of the same name. it just returns the literal$WHICHpiddle when it can, so beware of dataflow! β οΈ also: indices are NOT guaranteed to come back in any surface-logical order, though physically indexed dimensions should be sorted in physical-lexicographic order.whichVals()β returns$VALSindexed so it lines up with the indices thatwhichND()hands back. ngl the only reason you'd reach forwhichND()andwhichVals()instead of$WHICHand$VALSis if you need physical representations of dummy dimension indices. try to avoid it if you can, fr fr. πwhich()β as for the builtin PDL function.at(@index)β returns a perl scalar corresponding to the Nd index@index.set(@index, $value)β sets a non-missing value at index@indexto$value. itbarf()s if@indexpoints to a missing value. π
π¦ Ufuncs π¦
these come from PDL::CCS::Ufunc, they're implemented, and they ought to handle missing values correctly β meaning they act like their dense counterparts would:
prodoverproddprodoverdprodsumoversumdsumoverdsumandoveroroverbandoverborovermaximummaximum_ind ##-- goofy if "missing" value is maximalmaxminimumminimum_ind ##-- goofy if "missing" value is minimalminnbadovernbadngoodoverngoodnnzanyall
some Ufuncs are still unimplemented. check PDL::CCS::Ufunc for the details, bestie.
β¨ Unary Operations β¨
the unary ops that are supported, with their overloads:
bitnotβ overloads as~notβ overloads as!sqrtβ no overloadabsβ no overloadsinβ no overloadcosβ no overloadexpβ no overloadlogβ no overloadlog10β no overload
okay so real talk: any pointwise unary operation can be run directly on the $VALS piddle. and if you wanna wrap an operation MY_UNARY_OP on piddles into a PDL::CCS::Nd method, this is the idiom, iconic and simple:
package PDL::CCS::Nd;
*MY_UNARY_OP = _unary_op('MY_UNARY_OP', PDL->can('MY_UNARY_OP'));
also note: unary operations may change the "missing" value associated with the sparse matrix. and honestly that's easily seen to be the Right Way To Do It β picture unary not over a very sparse (say 99% missing) binary-valued matrix: it's much easier and more efficient to flip only the 1% of physically stored (non-missing) values plus the missing value, than to build a whole new matrix that's 99% non-missing, assuming $missing==0. slay. π
π₯ Binary Operations π₯
a bunch of basic binary ops on PDL::CCS::Nd objects are supported, and they only give correct results under the assumption that "missing" values $missing are annihilators for the operation in question. so like, if we wanna compute:
$c = OP($a,$b)
for a binary operation OP on PDL::CCS::Nd objects $a and $b, the current implementation only produces the correct $c if, for all values $av in $a and $bv in $b:
OP($av,$b->missing) == OP($a->missing,$b->missing) , and
OP($a->missing,$bv) == OP($a->missing,$b->missing)
this is true in general for OP==\&mult and $missing==0, but not e.g. for OP==\&plus and $missing==0. it should always hold for $missing==BAD (except in the case of assignment, which is a funny kind of operation anyways).
currently, the only way to make sure all values get computed correctly in the general case is for $a and $b to contain exactly the same physically indexed values β which kinda defeats the whole point of sparse storage, especially if implicit pseudo-threading is in the mix (because then we'd likely wind up instantiating β or at least inspecting β the entire dense matrix π). future implementations may relax these restrictions somewhat. no cap.
Arithmetic Operations
plusβ overloads as+minusβ overloads as-multβ overloads as*divideβ overloads as/moduloβ overloads as%powerβ overloads as**
Comparisons
gtβ overloads as>geβ overloads as>=ltβ overloads as<leβ overloads as<=eqβ overloads as==neβ overloads as!=spaceshipβ overloads as<=>
Bitwise Operations
and2β overloads as&or2β overloads as|xorβ overloads as^shiftleftβ overloads as<<shiftrightβ overloads as>>
Matrix Operations
innerβ (none)matmultβ overloads asx
Other Operations
rassgnβ overloads as.=stringβ overloads as""
all supported binary operation functions obey the PDL input calling conventions (i.e. they all accept a third argument $swap), and they delegate the computation to the underlying PDL functions. note that the PDL::CCS::Nd methods currently do NOT support a third "output" argument. π
ββοΈ
to wrap a new binary operation MY_BINOP into a PDL::CCS::Nd method, here's the idiom, go off:
package PDL::CCS::Nd;
*MY_BINOP = _ccsnd_binary_op_mia('MY_BINOP', PDL->can('MY_BINOP'));
the low-level alignment of physically indexed values for binary operations is handled by PDL::CCS::ccs_binop_align_block_mia(). computation happens block-wise at the perl level so we avoid over- rsp. underflow of the space requirements for the output PDL. π
π Low-Level Object Access π
these methods give you low-level access to the PDL::CCS::Nd object structure:
insertWhichβSignature: ($ccs; int whichND(Ndims,Nnz1); vals(Nnz1))
sets or inserts values in $ccs for the indices in $whichND to $vals. $whichND need not be sorted. implicitly makes $ccs physically indexed. returns the (destructively altered) $ccs.
appendWhichβSignature: ($ccs; int whichND(Ndims,Nnz1); vals(Nnz1))
like insertWhich(), but it assumes no values for any of the $whichND indices are already present in $ccs. this is faster (because indexNDi need not be called), but less safe. β οΈ
is_physically_indexed()β returns true iff only physical dimensions are present.to_physically_indexed()β just returns the calling object if all non-missing elements are already physically indexed. otherwise, returns a newPDL::CCS::Ndobject identical to the caller except that all non-missing elements are physically indexed. π«Ά heads up: this may gobble a large amount of memory if the calling element has large dummy dimensions. also ensures the physical dimension order is identical to the logical dimension order.make_physically_indexedβ wrapper forto_physically_indexed()which eliminates dummy dimensions destructively in the calling object.
Alias: make_physical().
pdims()β returns the$PDIMSpiddle. see "Object Structure", above.vdims()β returns the$VDIMSpiddle. see "Object Structure", above.setdims_p(@dims)β sets the$PDIMSpiddle. see "Object Structure", above. returns the calling object. Alias:setdims().nelem_p()β returns the number of physically addressable elements.nelem_v()β returns the number of virtually addressable elements. Alias fornelem()._ccs_nvperp()β returns number of virtually addressable elements per physically addressable element, which should be a positive integer.nstored_p()β returns actual number of physically addressed stored elements (aka$Nnzaka$WHICH->dim(1)).nstored_v()β returns actual number of physically+virtually addressed stored elements.nmissing_p()β returns number of physically addressable elements minus the number of physically stored elements.nmissing_v()β returns number of physically+virtually addressable elements minus the number of physically+virtually stored elements.allmissing()β returns true iff no non-missing values are stored.missing()/missing($missing)β get/set the value to use for missing elements. returns the (new) value for$missing._whichND()/_whichND($whichND)β get/set the underlying$WHICHpiddle._nzvals()/_nzvals($storedvals)β get/set the slice of the underlying$VALSpiddle corresponding for non-missing values only. Alias:whichVals()._vals()/_vals($storedvals)β get/set the underlying$VALSpiddle.hasptr($pdimi)β returns true iff a pointer for physical dim$pdimiis cached.ptr($pdimi)β get a pointer pair for a physically indexed dimension$pdimi. uses cached piddles in$PTRSif present, computes & caches otherwise.
$pdimi defaults to zero. if $pdimi is zero, then it should hold that:
all( $pi2nzi==sequence($ccs->nstored_p) )
getptr($pdimi)β guts forptr(). does not check$PTRSand does not cache anything.clearptr($pdimi)β clears any cached Harwell-Boeing pointers for physically indexed dimension$pdimi.clearptrs()β clears any cached Harwell-Boeing pointers.flags()/flags($flags)β get/set object-local$FLAGS.bad_is_missing()/bad_is_missing($bool)β get/set the value of the object-local "bad-is-missing" flag. if this flag is set, BAD values in$VALSare considered "missing", regardless of the current value of$missing.badmissing()β sets the "bad-is-missing" flag and returns the calling object.nan_is_missing()/nan_is_missing($bool)β get/set the value of the object-local "NaN-is-missing" flag. if this flag is set, NaN (and+inf,-inf) values in$VALSare considered "missing", regardless of the current value of$missing.nanmissing()β sets the "nan-is-missing" flag and returns the calling object.
π General Information π
density()β returns the number of non-missing values divided by the number of indexable values in the sparse object, as a perl scalar. the vibe check of sparsity, if you will. β¨
πΈ compressionRate()
okay so this one hands you back the compression rate of your PDL::CCS::Nd object, measured against a dense piddle of the physically indexable dimensions. β¨
- higher values = better compression (think: lower density). slay. π
- negative values = dense storage would be more memory-efficient π so like, the sparse situation is not it. facts are facts bestie.
- and btw, pointers are not included in the computation of the compression rate. that's just how it is. π«Ά
π ACKNOWLEDGEMENTS π
shoutouts, we love to see them:
- Perl by Larry Wall. the blueprint. π
- PDL by Karl Glazebrook, Tuomas J. Lukka, Christian Soeller, and others. legends only, fr fr. π
π KNOWN BUGS π
Many.
that's it. that's the whole section. iconic honestly π
π AUTHOR π
Bryan Jurish <moocow@cpan.org> π
π Copyright Policy π
Copyright (C) 2007-2024, Bryan Jurish. All rights reserved.
This package is free software, and entirely without warranty. You may redistribute it and/or modify it under the same terms as Perl itself. no cap. β¨
π¦ SEE ALSO π¦
perl(1)PDL(3perl)PDL::SVDLIBC(3perl)PDL::CCS::Nd(3perl)
and these two absolute gems:
- SVDLIBC:
http://tedlab.mit.edu/~dr/SVDLIBC/ - SVDPACKC:
http://www.netlib.org/svdpack/
aaaand that's the whole page bestie, we did it π«Ά mwah π this was Nd(3pm) β perl v5.40.0, dated 2025-01-04. go forth and sparse-ify, you've got this. πΈβ¨
π¦ where this page comes from
- debian trixie: libpdl-ccs-perl 1.24.1-1 Β· Artistic-1.0-Perl OR GPL-1.0-or-later
- ubuntu resolute: libpdl-ccs-perl 1.24.1-1 Β· Artistic-1.0-Perl OR GPL-1.0-or-later
- π girlified by deepseek-ai/DeepSeek-V4.1-Flash on : a language model's rewrite of the original, under its terms