On Wed, Dec 09, 2015 at 09:52:22AM +0100, Bryan Jurish wrote:
> morning all,
> 
> I've just released a new module PDL::SVDSLEPc
> ...
> <https://metacpan.org/release/PDL-SVDLIBC> on my machine here (I believe
> it's using ARPACK under the hood, which was mentioned on this list a few
> years back, but my searches have failed to turn up any PDL-ARPACK interface
> code).

I used arpack in May 2014 and posted a message (below) and an example
(attached) to the list:  

On Mon, 12 May 2014 14:16:09 -0500, Luis Mochan wrote:
 
> I once made some pdl code that interfaces with ARPACK to calculate
> photonic bands, but it may be used for eigenvalues of sparse matrices
> or of arbitrary functions which perform a linear transformation (i.e.,
> not necessarily using a matrix to respresent the transformation). I
> enclose an example that calculates the eigenvalues of the a second
> diference operator (discrete analogy to the second derivative, as in
> 1D phonon calculations) and compares with the analytical solution (I
> don't remember the details). Hope the interface proves useful.           

Best regards,
Luis

-- 

                                                                  o
W. Luis Mochán,                      | tel:(52)(777)329-1734     /<(*)
Instituto de Ciencias Físicas, UNAM  | fax:(52)(777)317-5388     `>/   /\
Apdo. Postal 48-3, 62251             |                           (*)/\/  \
Cuernavaca, Morelos, México          | [email protected]   /\_/\__/
GPG: 791EB9EB, C949 3F81 6D9B 1191 9A16  C2DF 5F0A C52B 791E B9EB


#!/usr/bin/env perl
# arpack for photonic bands with complex eigenvalues
use strict;
use warnings;
use feature q(say);
use PDL;
use PDL::NiceSlice;
use PDL::Graphics::PGPLOT;
use PDL::Complex;

*znaupdPP=\&PDL::znaupdPP;
*zneupdPP=\&PDL::zneupdPP;

my $N=100; #size of system
my $nev=50; #eigenvalues
my $ncv=2*$nev; #number of Lanczos vectors
# Mode=1 A*x = lambda*x, A symmetric
#show analytical solution (divide by 2 to account for degeneracy)
my $PI=4*atan2(1,1);
my $seq=2*$PI*(sequence($N))->qsort/$N/2;
my $res=sqrt(2*(1-cos($seq)));
line $res;
hold;

# setup arguments to eigen solver
my @args=(
	bmat=>'I', #standard problem
	n=>$N, #size of system
	nev=>$nev, #how many eigenvalues
	tol=>0, #1e-8, #default tolerance 0=> machine precission
	ncv=>$ncv, #Number of lanczos vectors
	ishift=> 1, #kind of shift
	mxiter=> 1250, #max number of iterations
	info=>0, #random initial residual
	function=>\&func #function to multiply matrix.vector
    );

#find smallest (algebraic) eigenvalues
# Which may be LM SM LR SR LI SI 

my ($values,$vectors)= areigens(@args, which=>'SR');
my $realpart=$values((0))->qsort;
printf "Eigenvalues=$realpart\n";
#printf "Eigenvectors=$vectors\n";
line $realpart->xvals+$N-$nev, sqrt(-$realpart(-1:0));

#find largest (algebraic) eigenvalues
my ($values1,$vectors1)= areigens(@args, which=>'LR');
my $realpart1=$values1((0))->qsort;
printf "Eigenvalues=$realpart1\n";
$realpart1->where($realpart1>0).=0;
#printf "Eigenvectors=$vectors1\n";
line $realpart1->xvals, sqrt(-$realpart1(-1:0));
print "listo?\n";
my $r=<>;

#interface
sub areigens {
    my $mode=1; #solve only type 1 problems, for the time being
    #First change hash argument into parameters
    my %arg=@_;
    my $ido=0;
    my $bmat=uc $arg{bmat};
    my $n=$arg{n}; #size of matrix
    my $which=$arg{which};
    my $nev=$arg{nev};
    my $tol=$arg{tol};
    my $resid=$arg{resid} || zeroes(double, 2, $n);  #input/output residuals  
    my $ncv=$arg{ncv}; #desired lanczos vectors
    my $v=zeroes(double, 2, $n, $ncv); #space for vectors
#    my $ldv=$n; # leading dimension of $v
    my $ishift=$arg{ishift}; #kind of shifts
    my $mxiter=$arg{mxiter}; #Max num. of arnoldi iterations
    my $iparam=pdl(long, $ishift, 0, $mxiter, 1, 0, 0, $mode, 0, 0, 0, 0);
    my $ipntr=zeroes(long,14); #output
    my $workd=zeroes(double, 2, 3*$n); # work array
    my $lworkl =$ncv*(3*$ncv+5);
    my $workl=zeroes(double, 2, $lworkl); #work space
    my $rwork=zeroes(double, $ncv);
    my $info=$arg{info}; #kind of initial residual, error flag
    my $function=$arg{function};

    # test inputs
    die "BMAT must be I or G, not $bmat" unless $bmat eq 'I' or $bmat eq 'G';
    my $t=0;
    $t ||= $which eq $_ foreach qw(LM SM LR SR LI SI);
    die "WHICH must be any of LM SM LR SR LI SI, not $which" unless $t;
    die "0<NEV<N violated by NEV=$nev" unless 0<$nev and $nev <= $n; 
    $t=$resid->dim(1);
    die "RESID must be of at least size N, not $t" unless $n<=$t;
    die "NEV<NCV<=N violated by $ncv" unless $nev<$ncv and $ncv<=$n;
    die "ISHIFT must be 0 or 1" unless $ishift==0 or $ishift == 1;
    die "MXITER must be nonnegative" unless 0<=$mxiter;
    die "MODE must be 1 for the time being" unless $mode == 1;

    #convert whatever is possible to a pdl
    ($ido, $n, $nev, $info)=
	map long($_), ($ido, $n, $nev, $info); 
    $tol=double($tol);
    while(1){
	znaupdPP($ido, $nev, $tol, $resid, $v, $iparam,
	     $ipntr, $workd, $workl, $rwork, $info, $bmat, $which); 
	last if $ido == 99;
	die "Unexpected \$ido=$ido" unless $ido==1 or $ido==-1;
	my $offx=$ipntr((1-1))-1; #careful with offsets
	my $offy=$ipntr((2-1))-1;
	my $x=$workd(:, $offx:$offx+$n-1); # vector to operate on
	my $y=$workd(:, $offy:$offy+$n-1); # vector to leave result
	$y.=&$function($x); #operate on $x and leave result in $y
			    #within workd;
    }
    die "Error in znaupd, \$info=$info" unless $info == 0;
    #Obtain the eigenvalues and eigenvectors
    my $rvec=long(1); #require eigenvectors
    my $howmny='A'; #compute all nev eigenvectors 
    my $select=ones(long, $ncv); #workspace or selection criteria
    my $d=zeroes(double, 2, $nev); #workspace for eigenvalues (nev+1???)
    my $z=zeroes(double, 2, $n,$nev); #eigenvectors
    my $sigma=zeroes(2); #shift. unused
    my $workev=zeroes(double,2,2*$ncv);
    zneupdPP($rvec, $select, $d, $z, $sigma, $workev, $tol, $resid, $v,
	     $iparam, $ipntr, $workd, $workl, $rwork, $info,
	     $howmny, $bmat, $which);
    die "Error in zneupd, \$info=$info" unless $info == 0;
    return ($d, $z);
}

sub func {
    # function to test dsaupd. Eigenvectors are cartesian
#    my $vector=shift @_;
#    my $a=sequence($vector->dim(0))+1;
#    my $y= $a*$vector; # scale along axes
#    return $y;
    #second derivative: periodic boundaries
    my $vector=(shift @_)->mv(0,1); #mv r-i part to simplify rotations below
    # might need to cast to complex value with cplx (with dataflow)
    # before making complex operations. Unneded in simple cases.
    return ($vector->rotate(1)-2*$vector+$vector->rotate(-1))->mv(1,0);
}


#call dsaupd  
#c     ( IDO, BMAT, N, WHICH, NEV, TOL, RESID, NCV, V, LDV, IPARAM,
#c       IPNTR, WORKD, WORKL, LWORKL, INFO )
#c

no PDL::NiceSlice;
use Inline Pdlpp=>Config=>
    #MYEXTLIB=>'/home/mochan/txt/cache/12/arpack/ARPACK/libarpack_Debian.a',
    LIBS=>'-larpack -lgfortran';
use Inline Pdlpp => <<'FIN_PP';
pp_def('znaupdPP', 
       Pars=>'int [o] ido(); int nev();  tol(); [o] resid(ri=2, n);'
            .'[o] v(ri=2, n,ncv); int [o] iparam(l=11); '
            .'int [o] ipntr(lp=14); [o] workd(ri=2,tn); [o] workl(ri=2,lw);'
            .'[o] rwork(ncv); int [o] info();', 
       OtherPars=>'char *bmat; char *which;',
       GenericTypes=>['D'],
       Code=> q{
          int N=$SIZE(n);
          int LDV=N;
          int NCV=$SIZE(ncv);
          int LWORKL=$SIZE(lw);
          znaupd_($P(ido), $COMP(bmat), &N, $COMP(which), $P(nev),
                  $P(tol), $P(resid), &NCV, $P(v), &LDV, $P(iparam),
                  $P(ipntr), $P(workd), $P(workl), &LWORKL, $P(rwork),
                  $P(info));
       },
    );
pp_def('zneupdPP',
       Pars=>'int rvec(); int select(ncv); [o] d(ri=2, nev); '
             .'[o] z(ri=2, n,nev); sigma(ri=2);'
             .'workev(ri=2,ncv2); tol(); resid(ri=2,n); '
             .'v(ri=2,n,ncv); int iparam(l=11);'
             .'int ipntr(lp=14); workd(ri=2,tn); workl(ri=2,lw);'
             .'rwork(ncv); int [o] info();',
       OtherPars=>'char *howmny; char *bmat; char *which; ',
       GenericTypes=>['D'],
       Code=> q{
           int N=$SIZE(n);
           int LDZ=N;
           int NCV=$SIZE(ncv);
           int NEV=$SIZE(nev);
           int LDV=N;
           int LWORKL=$SIZE(lw);
           zneupd_($P(rvec), $COMP(howmny), $P(select), $P(d), $P(z), 
                   &LDZ, $P(sigma), $P(workev), $COMP(bmat), &N, $COMP(which),
                   &NEV, $P(tol),$P(resid), &NCV, $P(v), &LDV, 
                   $P(iparam), $P(ipntr), $P(workd), $P(workl),
                   &LWORKL, $P(rwork), $P(info));
        },
    );

FIN_PP
------------------------------------------------------------------------------
_______________________________________________
pdl-general mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/pdl-general

Reply via email to