package Math::SparseVector; use 5.006; use strict; use warnings; require Exporter; our @ISA = qw(Exporter); # Items to export into callers namespace by default. Note: do not export # names by default without a very good reason. Use EXPORT_OK instead. # Do not simply export all your public functions/methods/constants. # This allows declaration use Math::SparseVector ':all'; # If you do not need this, moving things directly into @EXPORT or @EXPORT_OK # will save memory. our %EXPORT_TAGS = ( 'all' => [ qw( ) ] ); our @EXPORT_OK = ( @{ $EXPORT_TAGS{'all'} } ); our @EXPORT = qw( ); our $VERSION = '0.04'; use overload '++' => 'incr', '+' => 'add', 'fallback' => undef; # sparse vector contructor # creates an empty sparse vector sub new { my $class = shift; my $self = {}; bless($self,$class); return $self; } # sets value at given index sub set { my $self = shift; my $key = shift; my $value = shift; if(!defined $key || !defined $value) { print STDERR "Usage: vector->set(key,value)\n"; exit; } if($value==0) { print STDERR "Can not store 0 in the Math::SparseVector.\n"; exit; } $self->{$key} = $value; } # returns value at given index sub get { my $self = shift; my $key = shift; if(!defined $key) { print STDERR "Usage: vector->get(key)\n"; exit; } if(defined $self->{$key}) { return $self->{$key}; } return 0; } # returns indices of non-zero values in sorted order sub keys { my $self = shift; my @indices = keys %{$self}; my @sorted = sort {$a <=> $b} @indices; return @sorted; } # returns 1 if the vector is empty sub isnull { my $self = shift; my @indices = $self->keys; if(scalar(@indices)==0) { return 1; } return 0; } # prints sparse vector sub print { my $self = shift; foreach my $ind ($self->keys) { print "$ind " . $self->get($ind) . " "; } print "\n"; } # returns the equivalent string form sub stringify { my $self = shift; my $str=""; foreach my $ind ($self->keys) { $str.= "$ind " . $self->get($ind) . " "; } chop $str; return $str; } # increments value at given index sub incr { my $self = shift; my $key = shift; if(!defined $key) { print STDERR "Usage: vector->incr(key)\n"; exit; } $self->{$key}++; } # adds 2 sparse vectors sub add { my $self = shift; my $v2 = shift; if(!defined $v2) { print STDERR "Usage: v1->add(v2)\n"; exit; } foreach my $key ($v2->keys) { if(defined $self->{$key}) { $self->{$key}+=$v2->get($key); } else { $self->{$key}=$v2->get($key); } } } # returns the norm sub norm { my $self = shift; my $sum = 0; foreach my $key ($self->keys) { my $value = $self->{$key}; $sum += $value ** 2; } return sqrt $sum; } # normalizes given sparse vector sub normalize { my $self = shift; my $vnorm = $self->norm; if($vnorm != 0) { $self->div($vnorm); } } sub dot { my $self = shift; my $v2 = shift; if(!defined $v2) { print STDERR "Usage: v1->dot(v2)\n"; exit; } my $dotprod = 0; # optimize to do lesser comparisons by looping on # the smaller vector if (scalar($v2->keys) < scalar($self->keys)) { # v2 is smaller foreach my $key ($v2->keys) { if(defined $self->{$key}) { $dotprod += $v2->get($key) * $self->{$key}; } } } else { # self is smaller or equal to v2 foreach my $key ($self->keys) { if(defined $v2->{$key}) { $dotprod += $v2->get($key) * $self->{$key}; } } } return $dotprod; } # divides each vector entry by a given divisor sub div { my $self = shift; my $divisor = shift; if(!defined $divisor) { print STDERR "Usage: v1->div(DIVISOR)\n"; exit; } if($divisor==0) { print STDERR "Divisor 0 not allowed in Math::SparseVector::div().\n"; exit; } foreach my $key ($self->keys) { $self->{$key}/=$divisor; } } # adds a given sparse vector to a binary sparse vector sub binadd { my $v1 = shift; my $v2 = shift; if(!defined $v2) { print STDERR "Usage: v1->binadd(v2)\n"; exit; } foreach my $key ($v2->keys) { $v1->{$key}=1; } } # deallocates all the vector entries sub free { my $self = shift; %{$self}=(); undef %{$self}; } 1; __END__ =head1 NAME Math::SparseVector - Supports sparse vector operations such as setting a value in a vector, reading a value at a given index, obtaining all indices, addition and dot product of two sparse vectors, and vector normalization. =head1 MODULE HISTORY This module is the successor to Sparse::Vector, which was re-cast into this new namespace in order to introduce another module Math::SparseMatrix, which makes use of this module. =head1 SYNOPSIS use Math::SparseVector; # creating an empty sparse vector object $spvec=Math::SparseVector->new; # sets the value at index 12 to 5 $spvec->set(12,5); # returns value at index 12 $value = $spvec->get(12); # returns the indices of non-zero values in sorted order @indices = $spvec->keys; # returns 1 if the vector is empty and has no keys if($spvec->isnull) { print "vector is null.\n"; } else { print "vector is not null.\n"; } # print sparse vector to stdout $spvec->print; # returns the string form of sparse vector # same as print except the string is returned # rather than displaying on stdout $spvec->stringify; # adds sparse vectors v1, v2 and stores # result into v1 $v1->add($v2); # adds binary equivalent of v2 to v1 $v1->binadd($v2); # binary equivalnet treats all non-zero values # as 1s # increments the value at index 12 $spvec->incr(12); # divides each vector entry by a given divisor 4 $spvec->div(4); # returns norm of the vector $spvec_norm = $spvec->norm; # normalizes a sparse vector $spvec->normalize; # returns dot product of the 2 vectors $dotprod = $v1->dot($v2); # deallocates all entries $spvec->free; =head1 USAGE NOTES =over =item 1. Loading Math::SparseVector Module To use this module, you must insert the following line in your Perl program before using any of the supported methods. use Math::SparseVector; =item 2. Creating a Math::SparseVector Object The following line creates a new object of Math::SparseVector class referred with the name 'spvec'. $spvec=Math::SparseVector->new; The newly created 'spvec' vector will be initially empty. =item 3. Using Methods Now you can use any of the following methods on this 'spvec' Math::SparseVector object. =over =item 1. set(i,n) - Sets the value at index i to n # equivalent to $spvec{12}=5; $spvec->set(12,5); =item 2. get(i) - Returns the value at index i # equivalent to $value=$spvec{12}; $value = $spvec->get(12); =item 3. keys() - Returns the indices of all non-zero values in the vector # equivalent to @keys=sort {$a <=> $b} keys %spvec; @indices = $spvec->keys; =item 4. isnull() - Returns 1 if the vector is empty and has no keys # similar to # if(scalar(keys %spvec)==0) {print "vector is null.\n";} if($spvec->isnull) { print "vector is null.\n"; } =item 5. print() - Prints the sparse vector to stdout - Output will show a list of space separated 'index value' pairs for each non-zero 'value' in the vector. # similar to # foreach $ind (sort {$a<=>$b} keys %spvec) # { print "$ind " . $spvec{$ind} . " "; } $spvec->print; =item 6. stringify() - Returns the vector in a string form. Same as print() method except the vector is written to a string that is returned instead of displaying onto stdout # the below will do exactly same as $spvec->print; $string=$spvec->stringify; print "$string\n"; =item 7. v1->add(v2) - Adds contents of v2 to vector v1. Similar to v1+=v2 $v1->add($v2); If v1 = (2, , , 5, 8, , , , 1) & v2 = ( , 1, , 3, , , 5, , 9) where blanks show the 0 values that are not stored in Math::SparseVector. After $v1->add($v2); v1 = (2, 1, , 8, 8, , 5, , 10) and v2 remains same =item 8. v1->binadd(v2) - Binary equivalent of v2 is added into v1. Binary equivalent of a vector is obtained by setting all non-zero values to 1s. If v1 = (1, , , 1, 1, , , , 1) & v2 = ( , 1, , 1, , , 1, , 1) Then, after v1->binadd(v2), v1 will be (1, 1, , 1, 1, , 1, , 1). If v1 = (1, , , 1, 1, , , , 1) & v2 = ( , 1, , 3, , , 5, , 9) v1->binadd(v2); will set v1 to (1, 1, , 1, 1, , 1, , 1). =item 9. incr(i) - Increments the value at index i # is similar to $spvec{12}++; $spvec->incr(12); =item 10. div(n) - Divides each vector entry by a given divisor n $spvec->div(4); If spvec = (2, , , 5, 8, , , , 1) Then, $spvec->div(4) will set spvec to (0.5, , , 1.25, 2, , , , 0.25) =item 11. norm() - Returns the norm of a given vector $spvec_norm = $spvec->norm; If spvec = (2, , , 5, 8, , , , 1) $spvec->norm will return the value = sqrt(2^2 + 5^2 + 8^2 + 1) = sqrt(4 + 25 + 64 + 1) = 9.69536 =item 12. v1->dot(v2) - Returns the dot product of two vectors $dotprod = $v1->dot($v2); If v1 = (2, , , 5, 8, , , , 1) & v2 = ( , 1, , 3, , , 5, , 9) v1->dot(v2) returns 5*3 + 1*9 = 15 + 9 = 24 =item 13. free() - Deallocates all entries and makes the vector empty $spvec->free; will set spvec to null vector () =back =back =head1 AUTHORS Amruta Purandare, University of Pittsburgh amruta at cs.pitt.edu Ted Pedersen, University of Minnesota, Duluth tpederse at d.umn.edu Mahesh Joshi, Carnegie-Mellon University maheshj at cmu.edu =head1 COPYRIGHT AND LICENSE Copyright (c) 2006-2008, Amruta Purandare, Ted Pedersen, Mahesh Joshi This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to The Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. =cut