Compare commits

...
27 Commits
Author SHA1 Message Date
jiri 01dce70fbc Makefile.am update 2026-11-05 23:27:09 +01:00
jiri 74479c1a27 csrmat claude-coded 2026-11-05 23:23:55 +01:00
jiri 90bd68b5ef added doc/bib/mat.tex 2026-11-05 21:21:38 +01:00
jiri 2e13ec931f reverting mistake in gitignore 2026-11-05 21:17:03 +01:00
jiri cb28835fe9 documentation update 2026-11-05 21:14:13 +01:00
jiri 6812b9eb5e implemented strassen multiplication 2026-11-05 21:13:58 +01:00
jiri 723823dd50 configure.ac - search for openblas too 2026-11-05 20:32:26 +01:00
jiri 61960f83ce tensor: bugfix in Tucker 2026-11-05 20:00:07 +01:00
jiri d8136aaa58 updated gitignore 2026-11-05 17:35:50 +01:00
jiri f9d663ec67 updated gitignore 2026-11-05 17:29:01 +01:00
jiri 90c37c9ec8 float support in doc 2026-11-05 17:23:57 +01:00
jiri d8c21582c7 float support for optimize 2026-11-05 17:21:14 +01:00
jiri 0751cff8b8 float support for polynomial 2026-11-05 16:59:52 +01:00
jiri d11e7515de float support for fourindex 2026-11-05 16:47:10 +01:00
jiri b3b023bfc3 float support for tensor 2026-11-05 16:43:09 +01:00
jiri c711270e25 float support for sparse matrices 2026-11-05 16:37:24 +01:00
jiri 2ed864bda4 numerical thresholds collected into a traits template 2026-11-05 16:27:49 +01:00
jiri 48796fba1b float support for nonclass functions 2026-11-05 16:18:49 +01:00
jiri bb42a566bc mat: efficient complexify 2026-11-05 15:47:07 +01:00
jiri 5266767a4c float support for smat 2026-11-05 15:42:29 +01:00
jiri ee8c6279e1 float support for mat 2026-11-05 15:28:51 +01:00
jiri 9ada78c9bb float support for vec 2026-11-05 15:20:27 +01:00
jiri f6da197192 added missing float support in noncblas 2026-11-05 15:09:05 +01:00
jiri 5b3bd82bed just testing the push mirror 2026-11-04 14:44:29 +01:00
jiri c635795615 README - note about mirroring at github 2026-11-04 14:37:20 +01:00
jiri 05f5dd5ffc updated README 2026-11-04 13:49:56 +01:00
jiri 108913da00 some more cosmetic fixes 2026-10-31 06:27:16 +01:00
53 changed files with 7870 additions and 427 deletions

No files matched your search

+3
View File
@@ -77,4 +77,7 @@ doc_questions.md
instructions instructions
suspected_bugs.md suspected_bugs.md
optimize_discussion*.md optimize_discussion*.md
claude-resume
*.log
*.trs
# CVS default ignores end # CVS default ignores end
+24 -10
View File
@@ -14,11 +14,11 @@ decisions and plans agreed with the author, so that they survive a fresh clone.
## Build and test ## Build and test
- Build the library with `make -j 8` (after header changes most files rebuild; run it in the background). - Build the library with `make -j 8` (after header changes most files rebuild; run it in the background).
- Flags used: `-std=gnu++11 -O3 -DDEBUG -DNO_STRASSEN -DFORTRAN_ -DNONCLAPACK`, libs `-lcblas -lblas -llapack`. - Flags used: `-std=gnu++11 -O3 -DDEBUG -DFORTRAN_ -DNONCLAPACK`, libs `-lcblas -lblas -llapack`.
Quick test program: `g++ -std=gnu++11 -I<repo> -O0 -DDEBUG -DNO_STRASSEN -DFORTRAN_ -DNONCLAPACK X.cc <repo>/.libs/libla.a -lcblas -lblas -llapack` Quick test program: `g++ -std=gnu++11 -I<repo> -O0 -DDEBUG -DFORTRAN_ -DNONCLAPACK X.cc <repo>/.libs/libla.a -lcblas -lblas -llapack`
(add `-fsanitize=address` for memory checks). (add `-fsanitize=address` for memory checks).
- Regression test of the optimizers: `make check` builds `testoptimize`; `./testoptimize` must report - Regression test of the optimizers: `make check` builds `testoptimize`; `./testoptimize` must report
"123 passed, 0 failed" (exit code = number of failures). New optimizer tests go to testoptimize.cc; "137 passed, 0 failed" (exit code = number of failures). New optimizer tests go to testoptimize.cc;
test problems live in testoptimize.h. test problems live in testoptimize.h.
- `t` (t.cc) is a collection of if(0)/if(1) test blocks, most read n from stdin (`echo 10 | ./t`). - `t` (t.cc) is a collection of if(0)/if(1) test blocks, most read n from stdin (`echo 10 | ./t`).
- NONCBLAS check: copy the repo with `cp -a` (keeps timestamps, otherwise automake reruns and fails), - NONCBLAS check: copy the repo with `cp -a` (keeps timestamps, otherwise automake reruns and fails),
@@ -66,6 +66,12 @@ decisions and plans agreed with the author, so that they survive a fresh clone.
adds x at (i,j) and conj(x) at (j,i); NRSMat(SparseSMat) takes the lower triangle; operator* is the adds x at (i,j) and conj(x) at (j,i); NRSMat(SparseSMat) takes the lower triangle; operator* is the
general product A B (correct for hermitian, complex symmetric exp(iH) and general matrices); general product A B (correct for hermitian, complex symmetric exp(iH) and general matrices);
SparseSMat::gemm computes A^T B (A^\dagger B with 'c'); otimes_sparse = M + M^\dagger incl. doubled diagonal. SparseSMat::gemm computes A^T B (A^\dagger B with 'c'); otimes_sparse = M + M^\dagger incl. doubled diagonal.
- CSRMat (completed 2026-11): compressed row format, members NRVec rowptr/col/v (share independently, value-only operations keep
the index arrays shared), columns strictly ascending in each row, full matrix always stored; flag symmetric only DECLARES hermitian
(A=A^dagger, for davidson/lanczos), set by CSRMat(NRSMat) and for real types from a symmetric SparseMat, never checked. Built by
conversion (SparseMat, SparseSMat, NRMat, NRSMat, arrays); conversions drop |x|<=sparse_epsilon, arithmetic keeps cancelled zeros
(simplify). dot conjugates the left operand (as NRMat); permuteme_rows permutes only rows; CSR*CSR = Gustavson; types double, float,
complex<double>, complex<float>, CPU only (cuSPARSE format for later). Tests bugtests/core/csrmat1,3,4,5 and iterative/csrmat2.
- SparseMat with the symmetric flag is complex symmetric (no conjugation); its permuteme_* members use - SparseMat with the symmetric flag is complex symmetric (no conjugation); its permuteme_* members use
0-based matrix indices, NRPerm/CyclePerm are always 1-based. 0-based matrix indices, NRPerm/CyclePerm are always 1-based.
- Permutations: applypermutation(p,v,inverse) uses the same convention as NRVec::permuted (r_{p(i)}=v_i - Permutations: applypermutation(p,v,inverse) uses the same convention as NRVec::permuted (r_{p(i)}=v_i
@@ -83,17 +89,23 @@ decisions and plans agreed with the author, so that they survive a fresh clone.
- a-X and a/X (scalar minus/divided by object) are deleted on purpose; a single integer constructor is - a-X and a/X (scalar minus/divided by object) are deleted on purpose; a single integer constructor is
forbidden for NRMat, NRMat_from1, Mat3 (Mat3(double) works). forbidden for NRMat, NRMat_from1, Mat3 (Mat3(double) works).
- Real-storage gdiagonalize uses the dgeev convention (Re + i Im). - Real-storage gdiagonalize uses the dgeev convention (Re + i Im).
- The NRMat offset view constructor is a feature. MATPTR is kept (rarely used). CSRMat is unfinished and - The NRMat offset view constructor is a feature. MATPTR is kept (rarely used). CSRMat: see above. NRMat::strassen (2026-11-06): native Strassen-Winograd in strassen.cc, gemm-compatible
documented as unsupported, but kept. Strassen is experimental. CUDA (CUDALA) is deferred/experimental parameters + cutoff (default LA_traits<T>::strassen_cutoff()=2048 all types, to be tuned on HPC), explicit only (gemm stays classical),
CBLAS leaves on strided quadrants, device primitives separated for CUDA; NO_STRASSEN removed everywhere. CUDA (CUDALA) is deferred/experimental
until the author has NVIDIA RTX hardware (known CUDA issues collected in suspected_bugs.md F10). until the author has NVIDIA RTX hardware (known CUDA issues collected in suspected_bugs.md F10).
- The regression module (reg.*, regsurf.*) is experimental, not much tested in practice (remarked in the manual); - The regression module (reg.*, regsurf.*) is experimental, not much tested in practice (remarked in the manual);
its NEA oscillator strength f=dE_min/dE is a placeholder for the true one. its NEA oscillator strength f=dE_min/dE is a placeholder for the true one.
## Planned (only when the author asks) ## Planned (only when the author asks)
- float version: float and complex<float> must ALWAYS use BLAS/LAPACK (s*/c* routines, noncblas wrappers) - float version (IN PROGRESS since 2026-11-05): float and complex<float> must ALWAYS use BLAS/LAPACK (s*/c* routines,
like double/complex<double>, never generic loops. INSTANTIZE(float) in optimize.cc currently needs float noncblas wrappers) like double/complex<double>, never generic loops. Done: noncblas, vec, mat, smat, nonclass (+ complexify
versions of diagonalize, linear_solve, singular_decomposition. BLAS-based NRVec members are meant for BLAS for NRSMat/NRMat), sparsemat, sparsesmat, tensor, fourindex, polynomial, header templates (matexp,
floating types only, not integers. conjgrad, gmres, davidson, lanczos, diis: no changes needed beyond the defaults). Float twins are generated from the double specializations (placed right after them). Precision-
dependent defaults live in LA_precision<T> (la_traits.h, only double and float; double values unchanged). optimize done
2026-11-05: own trait opt_precision<T> in optimize.h, INSTANTIZE(float), float tests in testoptimize.cc (test_float);
float LM on badly scaled problems needs my_scaling=true. reg/regsurf, CUDA stay double-only.
Tensor Tucker bugs fixed 2026-11-06 (complex: factor = vt.transpose(false); unnamed tensors with inverseorder; bugtests/tensor/tucker.cc).
BLAS-based NRVec members are meant for floating types only, not integers.
- NRSMat: possible bool member `hermitian` toggling hermitian vs complex symmetric behaviour - NRSMat: possible bool member `hermitian` toggling hermitian vs complex symmetric behaviour
(Tensor(const NRSMat&) must then respect it). (Tensor(const NRSMat&) must then respect it).
- SparseSMat: possible member `symmetry_type` (0 none, 1 symmetric, 2 hermitian) for NRSMat conversion, - SparseSMat: possible member `symmetry_type` (0 none, 1 symmetric, 2 hermitian) for NRSMat conversion,
@@ -112,7 +124,9 @@ decisions and plans agreed with the author, so that they survive a fresh clone.
regression D1-D16, iterative A26-A30 + A38 (2026-10-29), misc C31, C38-C42, B4 (2026-10-30): all bugs closed; regression D1-D16, iterative A26-A30 + A38 (2026-10-29), misc C31, C38-C42, B4 (2026-10-30): all bugs closed;
all doc questions answered (2026-10-30), the manual has no open question boxes. all doc questions answered (2026-10-30), the manual has no open question boxes.
- configure.ac and Makefile.am were changed 2026-10-30 (--enable-fortran64int, TESTS = testoptimize, targets manual/doc, - configure.ac and Makefile.am were changed 2026-10-30 (--enable-fortran64int, TESTS = testoptimize, targets manual/doc,
doxygen, all-local builds both if pdflatex/doxygen found; MANUAL_TEX lists the .tex files - add new chapters there): run autoreconf. doxygen; all-local builds only the manual (if pdflatex found), doxygen only on request; MANUAL_TEX lists the .tex files - add new chapters there): run autoreconf.
- configure.ac (2026-11-06): OpenBLAS (with LAPACK+CBLAS) preferred after MKL, only -lopenblas linked; --without-openblas;
not with --enable-fortran64int (its parsing moved up). Test programs then link with -lopenblas instead of -lcblas -lblas -llapack.
- matexp.h: exp()/sincos() default horner=false (polynom, faster for dense); use horner=true for sparse matrices. - matexp.h: exp()/sincos() default horner=false (polynom, faster for dense); use horner=true for sparse matrices.
- AuxStorage temporaries go to $TMPDIR (else cwd) and are unlinked at once; AuxStorage and DIIS are not copyable. - AuxStorage temporaries go to $TMPDIR (else cwd) and are unlinked at once; AuxStorage and DIIS are not copyable.
- reg.cc/regsurf.cc cache raw pointers into their NRVec/NRMat members (set in Init): never reallocate those - reg.cc/regsurf.cc cache raw pointers into their NRVec/NRMat members (set in Init): never reallocate those
+3 -3
View File
@@ -30,7 +30,7 @@ AM_CXXFLAGS=
AM_CXXFLAGS += -g AM_CXXFLAGS += -g
AM_CXXFLAGS += $(OPTIMIZEOPT) $(CUDAOPT) $(FORINTOPT) $(DEBUGOPT) $(PROFILEOPT) $(MATPTROPT) AM_CXXFLAGS += $(OPTIMIZEOPT) $(CUDAOPT) $(FORINTOPT) $(DEBUGOPT) $(PROFILEOPT) $(MATPTROPT)
AM_CXXFLAGS += -DNO_STRASSEN -DFORTRAN_ AM_CXXFLAGS += -DFORTRAN_
AM_CXXFLAGS += $(CBLASOPT) $(CLAPACKOPT) AM_CXXFLAGS += $(CBLASOPT) $(CLAPACKOPT)
AM_CXXFLAGS += $(MKLOPT) AM_CXXFLAGS += $(MKLOPT)
@@ -52,9 +52,9 @@ version.cc: always
#include $(top_srcdir)/aminclude.am #include $(top_srcdir)/aminclude.am
#documentation: make manual (alias doc) builds doc/manual.pdf, make doxygen the doxygen pages in doxygen-doc, #documentation: make manual (alias doc) builds doc/manual.pdf, make doxygen the doxygen pages in doxygen-doc,
#make all builds both besides the library (each only if its tool was found by configure) #make all builds manual besides the library (each only if its tool was found by configure)
MANUAL_TEX = doc/biblio.tex doc/bitvector.tex doc/building.tex doc/concepts.tex doc/fourindex.tex doc/introduction.tex doc/iterative.tex doc/lamacros.tex doc/manual.tex doc/mat.tex doc/nonclass.tex doc/optimize.tex doc/permutation.tex doc/polynomial.tex doc/quaternion.tex doc/regression.tex doc/smat.tex doc/sparse.tex doc/tensor.tex doc/utilities.tex doc/vec.tex doc/vecmat3.tex \ MANUAL_TEX = doc/biblio.tex doc/bitvector.tex doc/building.tex doc/concepts.tex doc/fourindex.tex doc/introduction.tex doc/iterative.tex doc/lamacros.tex doc/manual.tex doc/mat.tex doc/nonclass.tex doc/optimize.tex doc/permutation.tex doc/polynomial.tex doc/quaternion.tex doc/regression.tex doc/smat.tex doc/sparse.tex doc/tensor.tex doc/utilities.tex doc/vec.tex doc/vecmat3.tex \
doc/bib/iterative.tex doc/bib/optimize.tex doc/bib/permutation.tex doc/bib/polynomial.tex doc/bib/quaternion.tex doc/bib/utilities.tex doc/bib/vecmat3.tex doc/bib/iterative.tex doc/bib/mat.tex doc/bib/optimize.tex doc/bib/permutation.tex doc/bib/polynomial.tex doc/bib/quaternion.tex doc/bib/sparse.tex doc/bib/utilities.tex doc/bib/vecmat3.tex
EXTRA_DIST += doc/manual.pdf $(MANUAL_TEX) EXTRA_DIST += doc/manual.pdf $(MANUAL_TEX)
doc/manual.pdf: $(MANUAL_TEX) doc/manual.pdf: $(MANUAL_TEX)
+5 -2
View File
@@ -69,8 +69,11 @@ USING:
NOTE: NOTE:
We named the classes NRMat, NRVec etc. since we started to use them together with the Numerical Recipes library. They can be used with Numerical Recipes in C++, however, they are completely independent of this library. We named the classes NRMat, NRVec etc. since we started to use them together with the Numerical Recipes library. They can be used with Numerical Recipes in C++, however, they are completely independent of this library.
This is a beta release. The library has already been in use, but not every function/method has been tested. The library has already been in use, but not every function/method has been tested.
There is no documentation except the source itself, since I originaly did not intend to distribute it. Recently, I used AI for code review, bugfixes, and generating documentation.
The repository is from now on being mirrored at github, while the primary server remains git.pittnerovi.com.
I will appreciate your bugfixes, exhancements, suggestions etc. I will appreciate your bugfixes, exhancements, suggestions etc.
Jiri Pittner Jiri Pittner
+36 -7
View File
@@ -40,14 +40,41 @@ then
MKLOPT="-DHAS_MKL" MKLOPT="-DHAS_MKL"
AC_SUBST([MKLOPT]) AC_SUBST([MKLOPT])
else else
echo "MKL LIBRARY OR HEADER NOT FOUND, will try ATLAS next!" echo "MKL LIBRARY OR HEADER NOT FOUND, will try OpenBLAS next!"
fi
FORINTOPT=""
forint64=""
AC_ARG_ENABLE([fortran64int],[ --enable-fortran64int to link with 64-bit-integer-BLAS+LAPACK ],[forint64=${enableval}],)
AC_ARG_ENABLE([fotran64int],[ --enable-fotran64int old misspelled alias of --enable-fortran64int ],[forint64=${enableval}],)
# OpenBLAS (with LAPACK and CBLAS included) is preferred if MKL is not present; not for 64-bit integers
openblas_present=0
AC_ARG_WITH([openblas],[ --without-openblas do not use OpenBLAS even if found (use ATLAS or reference BLAS)],[],[with_openblas=yes])
case "${forint64}" in
""|no) ;;
*) with_openblas=no ;;
esac
if test $mkl_present == 0 -a "${with_openblas}" != no
then
AC_CHECK_LIB([openblas], [dgeev_], [AC_CHECK_LIB([openblas], [cblas_dgemm], [openblas_present=1])])
fi
if test $openblas_present == 1
then
echo "OPENBLAS FOUND"
BLASLIB="-lopenblas"
AC_SUBST([BLASLIB])
CLAPACKOPT=-DNONCLAPACK
AC_SUBST([CLAPACKOPT])
else
echo "OPENBLAS NOT FOUND OR NOT USED, will try ATLAS next!"
fi fi
# Checks for mandatory libraries. # Checks for mandatory libraries.
ATLASLIB="" ATLASLIB=""
if test $mkl_present == 0 if test $mkl_present == 0 -a $openblas_present == 0
then then
AC_CHECK_LIB([atlas], [ATL_zgemv],ATLASLIB="-lblas -latlas", [ AC_CHECK_LIB([atlas], [ATL_zgemv],ATLASLIB="-lblas -latlas", [
echo "ATLAS not found, I hope you are using some other efficient BLAS!" echo "ATLAS not found, I hope you are using some other efficient BLAS!"
@@ -75,7 +102,13 @@ AC_ARG_ENABLE([matptr],[ --enable-matptr switch to double** matrix representa
CBLASLIB="" CBLASLIB=""
CBLASOPT="" CBLASOPT=""
if test $mkl_present == 0 if test $openblas_present == 1
then
AC_CHECK_HEADER([cblas.h],,[CBLASOPT=-DNONCBLAS], AC_INCLUDES_DEFAULT)
AC_SUBST([CBLASOPT])
fi
if test $mkl_present == 0 -a $openblas_present == 0
then then
AC_CHECK_LIB([cblas], [cblas_ddot], [CBLASLIB="-lcblas"], [CBLASOPT=-DNONCBLAS],[$ATLASLIB]) AC_CHECK_LIB([cblas], [cblas_ddot], [CBLASLIB="-lcblas"], [CBLASOPT=-DNONCBLAS],[$ATLASLIB])
AC_CHECK_HEADER([cblas.h],,[CBLASOPT=-DNONCBLAS CBLASLIB=""], AC_INCLUDES_DEFAULT) AC_CHECK_HEADER([cblas.h],,[CBLASOPT=-DNONCBLAS CBLASLIB=""], AC_INCLUDES_DEFAULT)
@@ -107,10 +140,6 @@ AC_SUBST([MATPTROPT])
#process options #process options
FORINTOPT=""
forint64=""
AC_ARG_ENABLE([fortran64int],[ --enable-fortran64int to link with 64-bit-integer-BLAS+LAPACK ],[forint64=${enableval}],)
AC_ARG_ENABLE([fotran64int],[ --enable-fotran64int old misspelled alias of --enable-fortran64int ],[forint64=${enableval}],)
if test -n "${forint64}"; then if test -n "${forint64}"; then
case "${forint64}" in case "${forint64}" in
yes) FORINTOPT="-DLONG_FORTRAN_INT" yes) FORINTOPT="-DLONG_FORTRAN_INT"
+1 -1
View File
@@ -34,7 +34,7 @@ namespace LA {
template<typename T, typename Matrix> template<typename T, typename Matrix>
extern bool conjgrad(const Matrix &bigmat, const NRVec<T> &b, NRVec<T> &x, const bool doguess=true, const double tol=1e-8, const int itmax=1000, const bool verbose=true, bool issquare=1,const bool precondition=1) extern bool conjgrad(const Matrix &bigmat, const NRVec<T> &b, NRVec<T> &x, const bool doguess=true, const double tol=LA_precision<typename LA_traits<T>::normtype>::conjgrad_tol(), const int itmax=1000, const bool verbose=true, bool issquare=1,const bool precondition=1)
{ {
int m=bigmat.nrows(); int m=bigmat.nrows();
int n=bigmat.ncols(); int n=bigmat.ncols();
+940 -23
View File
@@ -1,6 +1,6 @@
/* /*
LA: linear algebra C++ interface library LA: linear algebra C++ interface library
Copyright (C) 2008 Jiri Pittner <jiri.pittner@jh-inst.cas.cz> or <jiri@pittnerovi.com> Copyright (C) 2008-2026 Jiri Pittner <jiri.pittner@jh-inst.cas.cz> or <jiri@pittnerovi.com>
This program is free software: you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by
@@ -24,38 +24,955 @@
#include <fcntl.h> #include <fcntl.h>
#include <errno.h> #include <errno.h>
#include "csrmat.h" #include "csrmat.h"
#include "nonclass.h"
#include <algorithm>
namespace LA { namespace LA {
//raw pointers of possibly empty arrays (NULL when empty)
template<typename U> static inline const U *s_ptr(const NRVec<U> &x) {return x.size()? (const U *)x : NULL;}
template<typename U> static inline U *s_wptr(NRVec<U> &x) {return x.size()? (U *)x : NULL;}
template <typename T>
CSRMat<T>::CSRMat(const SPMatindex n, const SPMatindex m)
: nn(n), mm(m), symmetric(false), rowptr(n+1)
{
rowptr.clear();
}
template <typename T>
CSRMat<T>::CSRMat(const SPMatindex n)
: nn(n), mm(n), symmetric(false), rowptr(n+1)
{
rowptr.clear();
}
template <typename T>
CSRMat<T>::CSRMat(const SPMatindex n, const SPMatindex m, const NRVec<SPMatindex> &rp, const NRVec<SPMatindex> &c, const NRVec<T> &x)
: nn(n), mm(m), symmetric(false), rowptr(rp), col(c), v(x)
{
#ifdef DEBUG
if(rp.size()!=(int)n+1 || rp[0]!=0 || (int)rp[n]!=c.size() || c.size()!=x.size()) laerror("inconsistent arrays in CSRMat constructor");
for(SPMatindex i=0; i<n; ++i)
{
if(rp[i+1]<rp[i]) laerror("decreasing rowptr in CSRMat constructor");
for(SPMatindex k=rp[i]; k<rp[i+1]; ++k) if(c[k]>=m || (k>rp[i] && c[k]<=c[k-1])) laerror("column index out of range or columns not ascending in CSRMat constructor");
}
#endif
}
template <typename T>
void CSRMat<T>::copyonwrite(bool detachonly, bool deep)
{
if(rowptr.size()) rowptr.copyonwrite(detachonly);
if(col.size()) col.copyonwrite(detachonly);
if(v.size()) v.copyonwrite(detachonly,deep);
}
template <typename T>
void CSRMat<T>::resize(const SPMatindex n, const SPMatindex m)
{
nn=n; mm=m; symmetric=false;
col.resize(0); v.resize(0);
if(n==0 && m==0) {rowptr.resize(0); return;}
rowptr.resize(n+1);
rowptr.clear();
}
//counting sort, O(nnz+n+m); the columns of the result are ascending in each row also if they were not in the input
template <typename T>
const CSRMat<T> CSRMat<T>::transpose(bool conj) const
{
CSRMat<T> r(mm,nn);
r.symmetric=symmetric;
const int nz=v.size();
if(nz==0) return r;
const SPMatindex *rp=s_ptr(rowptr), *c=s_ptr(col);
const T *x=s_ptr(v);
r.col.resize(nz); r.v.resize(nz);
SPMatindex *rrp=r.rowptr, *rc=r.col;
T *rv=r.v;
for(int k=0; k<nz; ++k) ++rrp[c[k]+1];
for(SPMatindex j=0; j<mm; ++j) rrp[j+1]+=rrp[j];
for(SPMatindex i=0; i<nn; ++i) for(SPMatindex k=rp[i]; k<rp[i+1]; ++k)
{
SPMatindex p=rrp[c[k]]++;
rc[p]=i;
rv[p]= conj? LA_traits<T>::conjugate(x[k]) : x[k];
}
for(SPMatindex j=mm; j>0; --j) rrp[j]=rrp[j-1]; //rrp[j] was advanced to the start of row j+1
rrp[0]=0;
return r;
}
//removes small elements; also sums duplicates (adjacent in sorted rows, only during the construction from coordinates)
template <typename T>
void CSRMat<T>::simplify(const typename LA_traits<T>::normtype eps)
{
const int nz=v.size();
if(nz==0) return;
copyonwrite();
SPMatindex *rp=rowptr, *c=col;
T *x=v;
int p=0, k=0;
for(SPMatindex i=0; i<nn; ++i)
{
const int end=rp[i+1];
while(k<end)
{
const SPMatindex cc=c[k];
T s=x[k++];
while(k<end && c[k]==cc) s+=x[k++];
if(std::abs(s)>eps) {c[p]=cc; x[p]=s; ++p;}
}
rp[i+1]=p;
}
if(p<nz) {col.resize(p,true); v.resize(p,true);}
}
//from coordinate format: grouped by columns (i.e. the transposed matrix in CSR, rows unsorted), transposed back (rows sorted), duplicates summed
template <typename T>
void CSRMat<T>::fromcoo(const SPMatindex n, const SPMatindex m, const int nz, const SPMatindex *ri, const SPMatindex *ci, const T *vi)
{
CSRMat<T> t(m,n);
if(nz)
{
t.col.resize(nz); t.v.resize(nz);
SPMatindex *trp=t.rowptr, *tc=t.col;
T *tv=t.v;
for(int k=0; k<nz; ++k)
{
#ifdef DEBUG
if(ri[k]>=n || ci[k]>=m) laerror("index out of range in CSRMat construction");
#endif
++trp[ci[k]+1];
}
for(SPMatindex j=0; j<m; ++j) trp[j+1]+=trp[j];
for(int k=0; k<nz; ++k) {SPMatindex p=trp[ci[k]]++; tc[p]=ri[k]; tv[p]=vi[k];}
for(SPMatindex j=m; j>0; --j) trp[j]=trp[j-1];
trp[0]=0;
}
*this=t.transpose();
simplify();
}
//duplicates summed; a symmetric SparseMat (one triangle, complex symmetric) is unfolded without conjugation
template <typename T>
CSRMat<T>::CSRMat(const SparseMat<T> &rhs)
: nn(0), mm(0), symmetric(false)
{
const bool sym=rhs.issymmetric();
int nz=0;
for(const matel<T> *l=rhs.getlist(); l; l=l->next) nz+= (sym && l->row!=l->col)? 2:1;
NRVec<SPMatindex> ri(nz), ci(nz);
NRVec<T> vi(nz);
SPMatindex *r=s_wptr(ri), *c=s_wptr(ci);
T *x=s_wptr(vi);
int p=0;
for(const matel<T> *l=rhs.getlist(); l; l=l->next)
{
r[p]=l->row; c[p]=l->col; x[p++]=l->elem;
if(sym && l->row!=l->col) {r[p]=l->col; c[p]=l->row; x[p++]=l->elem;}
}
fromcoo(rhs.nrows(),rhs.ncols(),nz,r,c,x);
symmetric= sym && !LA_traits<T>::is_complex();
}
template <typename T>
CSRMat<T>::CSRMat(const SparseSMat<T> &rhs)
: nn(rhs.nrows()), mm(rhs.ncols()), symmetric(false), rowptr(nn+1)
{
const typename LA_traits<T>::normtype eps=LA_precision<typename LA_traits<T>::normtype>::sparse_epsilon();
typedef typename std::map<SPMatindex,T>::const_iterator IT;
int nz=0;
for(SPMatindex i=0; i<nn; ++i) {const std::map<SPMatindex,T> *l=rhs.line(i); if(l) for(IT q=l->begin(); q!=l->end(); ++q) if(std::abs(q->second)>eps) ++nz;}
col.resize(nz); v.resize(nz);
SPMatindex *rp=rowptr, *c=s_wptr(col);
T *x=s_wptr(v);
int p=0;
rp[0]=0;
for(SPMatindex i=0; i<nn; ++i)
{
const std::map<SPMatindex,T> *l=rhs.line(i);
if(l) for(IT q=l->begin(); q!=l->end(); ++q) if(std::abs(q->second)>eps) {c[p]=q->first; x[p++]=q->second;}
rp[i+1]=p;
}
}
template <typename T>
CSRMat<T>::CSRMat(const NRMat<T> &rhs)
: nn(rhs.nrows()), mm(rhs.ncols()), symmetric(false), rowptr(nn+1)
{
const typename LA_traits<T>::normtype eps=LA_precision<typename LA_traits<T>::normtype>::sparse_epsilon();
int nz=0;
for(SPMatindex i=0; i<nn; ++i) {const T *a=rhs[i]; for(SPMatindex j=0; j<mm; ++j) if(std::abs(a[j])>eps) ++nz;}
col.resize(nz); v.resize(nz);
SPMatindex *rp=rowptr, *c=s_wptr(col);
T *x=s_wptr(v);
int p=0;
rp[0]=0;
for(SPMatindex i=0; i<nn; ++i)
{
const T *a=rhs[i];
for(SPMatindex j=0; j<mm; ++j) if(std::abs(a[j])>eps) {c[p]=j; x[p++]=a[j];}
rp[i+1]=p;
}
}
//hermitian as NRSMat: the upper triangle is the conjugate of the stored lower one
template <typename T>
CSRMat<T>::CSRMat(const NRSMat<T> &rhs)
: nn(rhs.nrows()), mm(rhs.ncols()), symmetric(true), rowptr(nn+1)
{
const typename LA_traits<T>::normtype eps=LA_precision<typename LA_traits<T>::normtype>::sparse_epsilon();
int nz=0;
for(SPMatindex i=0; i<nn; ++i) for(SPMatindex j=0; j<nn; ++j) if(std::abs(rhs(i,j))>eps) ++nz;
col.resize(nz); v.resize(nz);
SPMatindex *rp=rowptr, *c=s_wptr(col);
T *x=s_wptr(v);
int p=0;
rp[0]=0;
for(SPMatindex i=0; i<nn; ++i)
{
for(SPMatindex j=0; j<nn; ++j)
{
const T a=rhs(i,j);
if(std::abs(a)>eps) {c[p]=j; x[p++]= j>i? LA_traits<T>::conjugate(a) : a;}
}
rp[i+1]=p;
}
}
template <typename T>
const T CSRMat<T>::operator()(const SPMatindex i, const SPMatindex j) const
{
#ifdef DEBUG
if(i>=nn || j>=mm) laerror("index out of range in CSRMat::operator()");
#endif
if(v.size()==0) return (T)0;
const SPMatindex *rp=rowptr, *c=col;
const T *x=v;
const SPMatindex *q=std::lower_bound(c+rp[i],c+rp[i+1],j);
if(q!=c+rp[i+1] && *q==j) return x[q-c];
return (T)0;
}
template <typename T>
void CSRMat<T>::put(int fd, bool dimensions, bool transp, bool orcaformat) const
{
if(orcaformat) laerror("ORCA format not supported for CSRMat");
if(transp) {transpose().put(fd,dimensions,false); return;}
errno=0;
if(dimensions)
{
if(sizeof(SPMatindex)!=write(fd,&nn,sizeof(SPMatindex))) laerror("cannot write() 1 in CSRMat::put");
if(sizeof(SPMatindex)!=write(fd,&mm,sizeof(SPMatindex))) laerror("cannot write() 2 in CSRMat::put");
}
int sym=symmetric;
if(sizeof(int)!=write(fd,&sym,sizeof(int))) laerror("cannot write() 3 in CSRMat::put");
rowptr.put(fd,true);
col.put(fd,true);
v.put(fd,true);
}
template <typename T>
void CSRMat<T>::get(int fd, bool dimensions, bool transp, bool orcaformat)
{
if(orcaformat) laerror("ORCA format not supported for CSRMat");
errno=0;
SPMatindex dim[2];
if(dimensions) {if(2*sizeof(SPMatindex)!=read(fd,&dim,2*sizeof(SPMatindex))) laerror("read() error 1 in CSRMat::get");}
else {dim[0]= transp?mm:nn; dim[1]= transp?nn:mm;}
int sym;
if(sizeof(int)!=read(fd,&sym,sizeof(int))) laerror("read() error 2 in CSRMat::get");
rowptr.get(fd,true);
col.get(fd,true);
v.get(fd,true);
nn=dim[0]; mm=dim[1]; symmetric=sym;
if(rowptr.size()!=(nn||mm ? (int)nn+1 : 0) || col.size()!=v.size()) laerror("inconsistent data in CSRMat::get");
if(transp) transposeme();
}
template <typename T>
CSRMat<T> & CSRMat<T>::operator*=(const T &a)
{
if(a==(T)0) {clear(); return *this;}
if(LA_traits<T>::imagpart(a)!=0) symmetric=false;
if(v.size()) v*=a;
return *this;
}
template <typename T>
CSRMat<T> & CSRMat<T>::operator=(const T &a)
{
if(nn!=mm) laerror("assignment of scalar to non-square CSRMat");
resize(nn,mm);
symmetric= LA_traits<T>::imagpart(a)==0;
if(nn==0 || std::abs(a)<=LA_precision<typename LA_traits<T>::normtype>::sparse_epsilon()) return *this;
col.resize(nn); v.resize(nn);
SPMatindex *rp=rowptr, *c=col;
T *x=v;
for(SPMatindex i=0; i<nn; ++i) {rp[i]=i; c[i]=i; x[i]=a;}
rp[nn]=nn;
return *this;
}
//in place if all diagonal elements are stored (only the values are copied if shared), otherwise via axpy
template <typename T>
CSRMat<T> & CSRMat<T>::operator+=(const T &a)
{
if(nn!=mm) laerror("addition of scalar to non-square CSRMat");
if(a==(T)0) return *this;
const SPMatindex *rp=s_ptr(rowptr), *c=s_ptr(col);
bool all= v.size()>0;
if(all) for(SPMatindex i=0; i<nn; ++i)
{
const SPMatindex *q=std::lower_bound(c+rp[i],c+rp[i+1],i);
if(q==c+rp[i+1] || *q!=i) {all=false; break;}
}
if(!all)
{
CSRMat<T> d(nn);
d=a;
const bool sym=symmetric;
axpy((T)1,d);
symmetric= sym && LA_traits<T>::imagpart(a)==0;
return *this;
}
if(LA_traits<T>::imagpart(a)!=0) symmetric=false;
v.copyonwrite();
T *x=v;
for(SPMatindex i=0; i<nn; ++i) x[std::lower_bound(c+rp[i],c+rp[i+1],i)-c]+=a;
return *this;
}
//merge of the sorted rows; for the same structure (shared or equal arrays) only the values by BLAS axpy
//the result is declared hermitian if both are (a matrix without elements counts as hermitian) and alpha is real
template <typename T>
void CSRMat<T>::axpy(const T alpha, const CSRMat<T> &x0, const bool transp)
{
if((!transp && (nn!=x0.nn || mm!=x0.mm)) || (transp && (nn!=x0.mm || mm!=x0.nn))) laerror("incompatible dimensions for axpy");
if(alpha==(T)0 || x0.v.size()==0) return;
CSRMat<T> xt;
if(transp) xt=x0.transpose();
const CSRMat<T> &x= transp? xt : x0;
const bool sym= (v.size()==0? nn==mm : symmetric) && x.symmetric && LA_traits<T>::imagpart(alpha)==0;
if(v.size()==0)
{
rowptr=x.rowptr; col=x.col; v=x.v;
if(alpha!=(T)1) v*=alpha;
symmetric=sym;
return;
}
const SPMatindex *rp=rowptr, *c=col, *xrp=x.rowptr, *xc=x.col;
const int nz=v.size(), xnz=x.v.size();
if(nz==xnz && ((rp==xrp && c==xc) || (!memcmp(rp,xrp,(nn+1)*sizeof(SPMatindex)) && !memcmp(c,xc,nz*sizeof(SPMatindex)))))
{
v.axpy(alpha,x.v);
symmetric=sym;
return;
}
const T *a=v, *b=x.v;
NRVec<SPMatindex> nrp(nn+1);
SPMatindex *np=nrp;
np[0]=0;
for(SPMatindex i=0; i<nn; ++i)
{
SPMatindex p=rp[i], q=xrp[i], cnt=0;
while(p<rp[i+1] && q<xrp[i+1]) {if(c[p]<xc[q]) ++p; else if(c[p]>xc[q]) ++q; else {++p; ++q;} ++cnt;}
np[i+1]=np[i]+cnt+(rp[i+1]-p)+(xrp[i+1]-q);
}
NRVec<SPMatindex> ncol(np[nn]);
NRVec<T> nv(np[nn]);
SPMatindex *nc=ncol;
T *y=nv;
int k=0;
for(SPMatindex i=0; i<nn; ++i)
{
SPMatindex p=rp[i], q=xrp[i];
while(p<rp[i+1] || q<xrp[i+1])
{
if(q==xrp[i+1] || (p<rp[i+1] && c[p]<xc[q])) {nc[k]=c[p]; y[k++]=a[p++];}
else if(p==rp[i+1] || c[p]>xc[q]) {nc[k]=xc[q]; y[k++]=alpha*b[q++];}
else {nc[k]=c[p]; y[k++]=a[p++]+alpha*b[q++];}
}
}
rowptr=nrp; col=ncol; v=nv;
symmetric=sym;
}
template <typename T>
const typename LA_traits<T>::normtype CSRMat<T>::norm(const T scalar) const
{
if(scalar==(T)0) return v.size()? v.norm() : (typename LA_traits<T>::normtype)0;
if(nn!=mm) laerror("subtraction of scalar from non-square CSRMat in norm()");
const SPMatindex *rp=s_ptr(rowptr), *c=s_ptr(col);
const T *x=s_ptr(v);
typename LA_traits<T>::normtype sum=0;
for(SPMatindex i=0; i<nn; ++i)
{
bool d=false;
if(x) for(SPMatindex k=rp[i]; k<rp[i+1]; ++k)
{
T e=x[k];
if(c[k]==i) {e-=scalar; d=true;}
sum+=LA_traits<T>::sqrabs(e);
}
if(!d) sum+=LA_traits<T>::sqrabs(scalar);
}
return std::sqrt(sum);
}
template <typename T>
const T CSRMat<T>::dot(const CSRMat<T> &rhs) const
{
if(nn!=rhs.nn || mm!=rhs.mm) laerror("dot of incompatible CSRMat matrices");
if(v.size()==0 || rhs.v.size()==0) return (T)0;
const SPMatindex *rp=rowptr, *c=col, *xrp=rhs.rowptr, *xc=rhs.col;
if(rp==xrp && c==xc) return v.dot(rhs.v);
const T *a=v, *b=rhs.v;
T s=0;
for(SPMatindex i=0; i<nn; ++i)
{
SPMatindex p=rp[i], q=xrp[i];
while(p<rp[i+1] && q<xrp[i+1])
{
if(c[p]<xc[q]) ++p;
else if(c[p]>xc[q]) ++q;
else s+=LA_traits<T>::conjugate(a[p++])*b[q++];
}
}
return s;
}
template <typename T>
const T CSRMat<T>::dot(const NRMat<T> &rhs) const
{
if((int)nn!=rhs.nrows() || (int)mm!=rhs.ncols()) laerror("dot of incompatible matrices");
if(v.size()==0) return (T)0;
const SPMatindex *rp=rowptr, *c=col;
const T *a=v;
T s=0;
for(SPMatindex i=0; i<nn; ++i)
{
const T *b=rhs[i];
for(SPMatindex k=rp[i]; k<rp[i+1]; ++k) s+=LA_traits<T>::conjugate(a[k])*b[c[k]];
}
return s;
}
template <typename T>
const T CSRMat<T>::trace() const
{
if(v.size()==0) return (T)0;
const SPMatindex *rp=rowptr, *c=col;
const T *x=v;
T s=0;
for(SPMatindex i=0; i<nn && i<mm; ++i)
{
const SPMatindex *q=std::lower_bound(c+rp[i],c+rp[i+1],i);
if(q!=c+rp[i+1] && *q==i) s+=x[q-c];
}
return s;
}
//sparse times sparse, row-wise with a dense accumulator: F. G. Gustavson, ACM Trans. Math. Softw. 4, 250 (1978)
//two passes (count, then compute); the columns of each result row are sorted; the result is not declared hermitian
template <typename T>
void CSRMat<T>::gemm(const T beta, const CSRMat<T> &a0, const char transa, const CSRMat<T> &b0, const char transb, const T alpha)
{
const char ta=tolower(transa), tb=tolower(transb);
CSRMat<T> at, bt;
if(ta!='n') at=a0.transpose(ta=='c');
if(tb!='n') bt=b0.transpose(tb=='c');
const CSRMat<T> &a= ta!='n'? at : a0;
const CSRMat<T> &b= tb!='n'? bt : b0;
if(a.mm!=b.nn) laerror("incompatible matrices in CSRMat::gemm");
if(beta!=(T)0 && (nn!=a.nn || mm!=b.mm)) laerror("incompatible result matrix in CSRMat::gemm");
const SPMatindex n=a.nn, m=b.mm;
CSRMat<T> r(n,m);
if(alpha!=(T)0 && a.v.size() && b.v.size())
{
const SPMatindex *arp=a.rowptr, *ac=a.col, *brp=b.rowptr, *bc=b.col;
const T *av=a.v, *bv=b.v;
NRVec<SPMatindex> marker(m);
SPMatindex *mk=marker;
for(SPMatindex j=0; j<m; ++j) mk[j]=n;
SPMatindex *rp=r.rowptr;
for(SPMatindex i=0; i<n; ++i)
{
SPMatindex cnt=0;
for(SPMatindex k=arp[i]; k<arp[i+1]; ++k) for(SPMatindex q=brp[ac[k]]; q<brp[ac[k]+1]; ++q) if(mk[bc[q]]!=i) {mk[bc[q]]=i; ++cnt;}
rp[i+1]=rp[i]+cnt;
}
r.col.resize(rp[n]); r.v.resize(rp[n]);
if(rp[n])
{
SPMatindex *c=r.col;
T *y=r.v;
NRVec<T> acc(m);
T *ac2=acc;
for(SPMatindex j=0; j<m; ++j) mk[j]=n;
for(SPMatindex i=0; i<n; ++i)
{
SPMatindex len=0;
for(SPMatindex k=arp[i]; k<arp[i+1]; ++k)
{
const T x=av[k];
for(SPMatindex q=brp[ac[k]]; q<brp[ac[k]+1]; ++q)
{
const SPMatindex j=bc[q];
if(mk[j]!=i) {mk[j]=i; c[rp[i]+len++]=j; ac2[j]=x*bv[q];}
else ac2[j]+=x*bv[q];
}
}
std::sort(c+rp[i],c+rp[i]+len);
for(SPMatindex t=rp[i]; t<rp[i]+len; ++t) y[t]=alpha*ac2[c[t]];
}
}
}
if(beta==(T)0) *this=r;
else
{
*this*=beta;
axpy((T)1,r);
}
symmetric=false;
}
template <typename T>
CSRMat<T> & CSRMat<T>::oplusequal(const CSRMat<T> &rhs)
{
const bool sym= (v.size()==0? nn==mm : symmetric) && rhs.symmetric;
const SPMatindex n=nn+rhs.nn, m=mm+rhs.mm;
const int nz=v.size(), rnz=rhs.v.size();
NRVec<SPMatindex> nrp(n+1), ncol(nz+rnz);
NRVec<T> nv(nz+rnz);
SPMatindex *np=nrp, *nc=s_wptr(ncol);
T *y=s_wptr(nv);
const SPMatindex *rp=s_ptr(rowptr), *c=s_ptr(col), *rrp=s_ptr(rhs.rowptr), *rc=s_ptr(rhs.col);
const T *x=s_ptr(v), *rx=s_ptr(rhs.v);
for(SPMatindex i=0; i<=nn; ++i) np[i]= rp? rp[i] : 0;
for(SPMatindex i=1; i<=rhs.nn; ++i) np[nn+i]=nz+rrp[i];
for(int k=0; k<nz; ++k) {nc[k]=c[k]; y[k]=x[k];}
for(int k=0; k<rnz; ++k) {nc[nz+k]=mm+rc[k]; y[nz+k]=rx[k];}
nn=n; mm=m;
rowptr=nrp; col=ncol; v=nv;
symmetric=sym;
return *this;
}
template <typename T>
const CSRMat<T> CSRMat<T>::otimes(const CSRMat<T> &rhs) const
{
CSRMat<T> r(nn*rhs.nn,mm*rhs.mm);
r.symmetric= symmetric && rhs.symmetric;
const int nz=v.size(), rnz=rhs.v.size();
if(nz==0 || rnz==0) return r;
const SPMatindex *rp=rowptr, *c=col, *brp=rhs.rowptr, *bc=rhs.col;
const T *x=v, *bx=rhs.v;
r.col.resize(nz*rnz); r.v.resize(nz*rnz);
SPMatindex *np=r.rowptr, *nc=r.col;
T *y=r.v;
int p=0;
for(SPMatindex i=0; i<nn; ++i) for(SPMatindex k=0; k<rhs.nn; ++k)
{
for(SPMatindex a=rp[i]; a<rp[i+1]; ++a) for(SPMatindex b=brp[k]; b<brp[k+1]; ++b) {nc[p]=c[a]*rhs.mm+bc[b]; y[p++]=x[a]*bx[b];}
np[i*rhs.nn+k+1]=p;
}
return r;
}
//row r becomes row to[r]: the rows are moved as blocks, their columns stay sorted
template <typename T>
void CSRMat<T>::permrows(const SPMatindex *to)
{
const int nz=v.size();
NRVec<SPMatindex> nrp(nn+1);
SPMatindex *np=nrp;
const SPMatindex *rp=rowptr;
np[0]=0;
for(SPMatindex r=0; r<nn; ++r) np[to[r]+1]=rp[r+1]-rp[r];
for(SPMatindex r=0; r<nn; ++r) np[r+1]+=np[r];
if(nz)
{
NRVec<SPMatindex> ncol(nz);
NRVec<T> nv(nz);
SPMatindex *nc=ncol;
T *y=nv;
const SPMatindex *c=col;
const T *x=v;
for(SPMatindex r=0; r<nn; ++r) for(SPMatindex k=rp[r], t=np[to[r]]; k<rp[r+1]; ++k, ++t) {nc[t]=c[k]; y[t]=x[k];}
col=ncol; v=nv;
}
rowptr=nrp;
}
template <typename T>
void CSRMat<T>::permuteme_rows(const NRPerm<int> &p, const bool inverse)
{
if((SPMatindex)p.size()!=nn) laerror("inconsistent dimension in permuteme_rows");
NRPerm<int> pinv;
if(inverse) pinv=p.inverse();
const NRPerm<int> &pp= inverse?pinv:p;
NRVec<SPMatindex> to(nn);
for(SPMatindex r=0; r<nn; ++r) to[r]=pp[r+1]-1;
if(nn) permrows(to);
symmetric=false;
}
//columns via the transposed matrix (counting sorts keep the rows sorted)
template <typename T>
void CSRMat<T>::permuteme_cols(const NRPerm<int> &p, const bool inverse)
{
if((SPMatindex)p.size()!=mm) laerror("inconsistent dimension in permuteme_cols");
*this=transpose();
permuteme_rows(p,inverse);
*this=transpose();
}
template <typename T>
void CSRMat<T>::permuteme_both(const NRPerm<int> &p, const NRPerm<int> &q, const bool inverse)
{
const bool sym= symmetric && p==q;
permuteme_rows(p,inverse);
permuteme_cols(q,inverse);
symmetric=sym;
}
template <typename T>
void CSRMat<T>::permuterows(const NRVec<SPMatindex> &p)
{
if((SPMatindex)p.size()!=nn) laerror("inconsistent dimension in permuterows");
if(nn) permrows(p);
symmetric=false;
}
template <typename T>
void CSRMat<T>::permutecolumns(const NRVec<SPMatindex> &p)
{
if((SPMatindex)p.size()!=mm) laerror("inconsistent dimension in permutecolumns");
*this=transpose();
permuterows(p);
*this=transpose();
}
template <typename T>
void CSRMat<T>::permuteindices(const NRVec<SPMatindex> &p)
{
if((SPMatindex)p.size()!=nn || (SPMatindex)p.size()!=mm) laerror("inconsistent dimension in permuteindices");
const bool sym=symmetric;
permuterows(p);
permutecolumns(p);
symmetric=sym;
}
//square: diagonal (binary search in each row); non-square: diagonal of A^T A (for preconditioning as SparseMat::diagonalof); cache is ignored
template <typename T>
const T* CSRMat<T>::diagonalof(NRVec<T> &r, const bool divide, bool cache) const
{
#ifdef DEBUG
if((int)mm!=r.size()) laerror("incompatible vector size in diagonalof()");
#endif
NOT_GPU(r);
r.copyonwrite();
T *rr=r;
const SPMatindex *rp=s_ptr(rowptr), *c=s_ptr(col);
const T *x=s_ptr(v);
if(nn==mm)
{
for(SPMatindex i=0; i<nn; ++i)
{
T d=0;
if(x)
{
const SPMatindex *q=std::lower_bound(c+rp[i],c+rp[i+1],i);
if(q!=c+rp[i+1] && *q==i) d=x[q-c];
}
if(divide) {if(d!=(T)0) rr[i]/=d;}
else rr[i]=d;
}
}
else
{
NRVec<T> d(mm);
d.clear();
T *dd=d;
for(int k=0; k<v.size(); ++k) dd[c[k]]+=x[k]*x[k];
if(divide) {for(SPMatindex j=0; j<mm; ++j) if(dd[j]!=(T)0) rr[j]/=dd[j];}
else for(SPMatindex j=0; j<mm; ++j) rr[j]=dd[j];
}
return divide?NULL:rr;
}
//r = beta r + alpha op(A) x, op = 'n', 't' or 'c' (conjugate transpose, the same as 't' for real types)
template <typename T>
void NRVec<T>::gemv(const T beta, const CSRMat<T> &a, const char trans, const T alpha, const NRVec<T> &x)
{
const char tr=tolower(trans);
const bool tp= tr!='n';
const bool cj= tr=='c' && LA_traits<T>::is_complex();
if((tp?a.nrows():a.ncols())!=(SPMatindex)x.size()) laerror("incompatible sizes in gemv");
#ifdef DEBUG
if((tp?a.ncols():a.nrows())!=(SPMatindex)nn) laerror("incompatible result vector in gemv");
#endif
NOT_GPU(*this);
NOT_GPU(x);
copyonwrite();
if(beta==(T)0) clear();
else if(beta!=(T)1) (*this)*=beta;
if(alpha==(T)0 || a.length()==0) return;
const SPMatindex *rp=a.getrowptr(), *c=a.getcol();
const T *av=a.getval();
const T *xv=x.v;
const SPMatindex n=a.nrows();
if(!tp)
for(SPMatindex i=0; i<n; ++i)
{
T s=0;
for(SPMatindex k=rp[i]; k<rp[i+1]; ++k) s+=av[k]*xv[c[k]];
v[i]+=alpha*s;
}
else
for(SPMatindex i=0; i<n; ++i)
{
const T ax=alpha*xv[i];
if(cj) for(SPMatindex k=rp[i]; k<rp[i+1]; ++k) v[c[k]]+=LA_traits<T>::conjugate(av[k])*ax;
else for(SPMatindex k=rp[i]; k<rp[i+1]; ++k) v[c[k]]+=av[k]*ax;
}
}
//vector from a matrix with one row or one column
template <typename T>
NRVec<T>::NRVec(const CSRMat<T> &rhs)
: NRVec(rhs.nrows()>1?rhs.nrows():rhs.ncols())
{
if(rhs.nrows()>1 && rhs.ncols()>1) laerror("cannot construct a vector from a sparse matrix with more than one row/column");
clear();
const SPMatindex *rp=s_ptr(rhs.getrowptr()), *c=s_ptr(rhs.getcol());
const T *x=s_ptr(rhs.getval());
if(!x) return;
if(rhs.nrows()>1) {for(SPMatindex i=0; i<rhs.nrows(); ++i) if(rp[i+1]>rp[i]) v[i]=x[rp[i]];}
else for(SPMatindex k=0; k<rp[1]; ++k) v[c[k]]=x[k];
}
//conversions to the other classes
template <typename T>
NRMat<T>::NRMat(const CSRMat<T> &rhs)
{
nn=rhs.nrows();
mm=rhs.ncols();
count=new int(1);
#ifdef CUDALA
location=cpu;
#endif
T *p;
#ifdef MATPTR
v= new T*[nn];
p=v[0] = new T[mm*nn];
for (int i=1; i< nn; i++) v[i] = v[i-1] + mm;
#else
p= v = new T[mm*nn];
#endif
memset(p,0,nn*mm*sizeof(T));
const SPMatindex *rp=s_ptr(rhs.getrowptr()), *c=s_ptr(rhs.getcol());
const T *x=s_ptr(rhs.getval());
if(x) for(int i=0; i<nn; ++i) for(SPMatindex k=rp[i]; k<rp[i+1]; ++k) p[(size_t)i*mm+c[k]]=x[k];
}
template <typename T>
NRMat<T> & NRMat<T>::operator+=(const CSRMat<T> &rhs)
{
if(nn!=(int)rhs.nrows() || mm!=(int)rhs.ncols()) laerror("incompatible matrices in +=");
NOT_GPU(*this);
copyonwrite();
const SPMatindex *rp=s_ptr(rhs.getrowptr()), *c=s_ptr(rhs.getcol());
const T *x=s_ptr(rhs.getval());
if(x) for(int i=0; i<nn; ++i) {T *a=(*this)[i]; for(SPMatindex k=rp[i]; k<rp[i+1]; ++k) a[c[k]]+=x[k];}
return *this;
}
template <typename T>
NRMat<T> & NRMat<T>::operator-=(const CSRMat<T> &rhs)
{
if(nn!=(int)rhs.nrows() || mm!=(int)rhs.ncols()) laerror("incompatible matrices in -=");
NOT_GPU(*this);
copyonwrite();
const SPMatindex *rp=s_ptr(rhs.getrowptr()), *c=s_ptr(rhs.getcol());
const T *x=s_ptr(rhs.getval());
if(x) for(int i=0; i<nn; ++i) {T *a=(*this)[i]; for(SPMatindex k=rp[i]; k<rp[i+1]; ++k) a[c[k]]-=x[k];}
return *this;
}
//C = beta C + alpha op(A) op(B), A sparse: rows of op(B) accumulated (op(B) transposed first if needed)
template <typename T>
void NRMat<T>::gemm(const T &beta, const CSRMat<T> &a, const char transa, const NRMat<T> &b, const char transb, const T &alpha)
{
const char ta=tolower(transa), tb=tolower(transb);
const bool tpa= ta!='n', cja= ta=='c' && LA_traits<T>::is_complex();
const int an= tpa? a.ncols() : a.nrows(), ak= tpa? a.nrows() : a.ncols();
const int bk= tb!='n'? b.ncols() : b.nrows(), bm= tb!='n'? b.nrows() : b.ncols();
if(ak!=bk) laerror("incompatible matrices in NRMat::gemm with CSRMat");
#ifdef DEBUG
if(nn!=an || mm!=bm) laerror("incompatible result matrix in NRMat::gemm with CSRMat");
#endif
NOT_GPU(*this);
copyonwrite();
if(beta==(T)0) clear();
else if(beta!=(T)1) (*this)*=beta;
if(alpha==(T)0 || a.length()==0) return;
NRMat<T> bt;
if(tb!='n') bt=b.transpose(tb=='c');
const NRMat<T> &bb= tb!='n'? bt : b;
const SPMatindex *rp=a.getrowptr(), *c=a.getcol();
const T *x=a.getval();
for(SPMatindex i=0; i<a.nrows(); ++i) for(SPMatindex k=rp[i]; k<rp[i+1]; ++k)
{
const T s= alpha*(cja? LA_traits<T>::conjugate(x[k]) : x[k]);
T *d= (*this)[tpa? c[k] : i];
const T *e= bb[tpa? i : c[k]];
for(int j=0; j<mm; ++j) d[j]+=s*e[j];
}
}
template <typename T>
const NRMat<T> NRMat<T>::operator*(const CSRMat<T> &rhs) const
{
if((SPMatindex)mm!=rhs.nrows()) laerror("incompatible matrices in NRMat*CSRMat");
NOT_GPU(*this);
NRMat<T> r(nn,rhs.ncols());
r.clear();
if(rhs.length()==0) return r;
const SPMatindex *rp=rhs.getrowptr(), *c=rhs.getcol();
const T *x=rhs.getval();
for(int i=0; i<nn; ++i)
{
const T *a=(*this)[i];
T *d=r[i];
for(int k=0; k<mm; ++k) if(a[k]!=(T)0) for(SPMatindex q=rp[k]; q<rp[k+1]; ++q) d[c[q]]+=a[k]*x[q];
}
return r;
}
//lower triangle, as from SparseSMat
template <typename T>
NRSMat<T>::NRSMat(const CSRMat<T> &rhs)
: nn(rhs.nrows())
{
if(rhs.nrows()!=rhs.ncols()) laerror("cannot transform rectangular matrix to NRSMat");
#ifdef CUDALA
location=cpu;
#endif
count=new int(1);
v=new T[NN2];
memset(v,0,NN2*sizeof(T));
const SPMatindex *rp=s_ptr(rhs.getrowptr()), *c=s_ptr(rhs.getcol());
const T *x=s_ptr(rhs.getval());
if(x) for(int i=0; i<nn; ++i) for(SPMatindex k=rp[i]; k<rp[i+1] && (int)c[k]<=i; ++k) v[(size_t)i*(i+1)/2+c[k]]=x[k];
}
template <typename T>
SparseMat<T>::SparseMat(const CSRMat<T> &rhs)
: nn(rhs.nrows()), mm(rhs.ncols()), symmetric(0), nonzero(0), count(new int(1)), list(NULL), rowsorted(NULL), colsorted(NULL)
{
for(typename CSRMat<T>::iterator p=rhs.begin(); p.notend(); ++p) add(p->row,p->col,p->elem);
}
template <typename T>
SparseSMat<T>::SparseSMat(const CSRMat<T> &rhs)
: nn(rhs.nrows()), mm(rhs.ncols()), count(new int(1))
{
v= new std::map<SPMatindex,T> * [nn];
memset(v,0,nn*sizeof(std::map<SPMatindex,T> *));
for(typename CSRMat<T>::iterator p=rhs.begin(); p.notend(); ++p) add(p->row,p->col,p->elem,false);
}
//real and imaginary parts, complex matrices from real ones (nonclass.h): the index arrays stay shared,
//unless elements which became zero have to be removed
template<typename R>
static const CSRMat<R> s_part(const CSRMat<std::complex<R> > &a, const NRVec<R> &x, const bool sym)
{
CSRMat<R> r(a.nrows(),a.ncols());
if(a.length())
{
r=CSRMat<R>(a.nrows(),a.ncols(),a.getrowptr(),a.getcol(),x);
const R eps=LA_precision<R>::sparse_epsilon();
const R *y=x;
for(int k=0; k<x.size(); ++k) if(std::abs(y[k])<=eps) {r.simplify(); break;}
}
if(sym && a.nrows()==a.ncols()) r.setsymmetric();
return r;
}
template<typename R>
static const CSRMat<std::complex<R> > s_cmplx(const CSRMat<R> &a, const bool imag)
{
CSRMat<std::complex<R> > r(a.nrows(),a.ncols());
if(a.length()) r=CSRMat<std::complex<R> >(a.nrows(),a.ncols(),a.getrowptr(),a.getcol(),NRVec<std::complex<R> >(a.getval(),imag));
if(!imag && a.issymmetric()) r.setsymmetric();
return r;
}
template<>
const CSRMat<double> realpart<CSRMat<std::complex<double> > >(const CSRMat<std::complex<double> > &a)
{return s_part(a,a.length()? realpart(a.getval()) : NRVec<double>(),a.issymmetric());}
template<>
const CSRMat<float> realpart<CSRMat<std::complex<float> > >(const CSRMat<std::complex<float> > &a)
{return s_part(a,a.length()? realpart(a.getval()) : NRVec<float>(),a.issymmetric());}
template<>
const CSRMat<double> imagpart<CSRMat<std::complex<double> > >(const CSRMat<std::complex<double> > &a)
{return s_part(a,a.length()? imagpart(a.getval()) : NRVec<double>(),false);}
template<>
const CSRMat<float> imagpart<CSRMat<std::complex<float> > >(const CSRMat<std::complex<float> > &a)
{return s_part(a,a.length()? imagpart(a.getval()) : NRVec<float>(),false);}
template<>
const CSRMat<std::complex<double> > realmatrix<CSRMat<double> >(const CSRMat<double> &a)
{return s_cmplx(a,false);}
template<>
const CSRMat<std::complex<float> > realmatrix<CSRMat<float> >(const CSRMat<float> &a)
{return s_cmplx(a,false);}
template<>
const CSRMat<std::complex<double> > imagmatrix<CSRMat<double> >(const CSRMat<double> &a)
{return s_cmplx(a,true);}
template<>
const CSRMat<std::complex<float> > imagmatrix<CSRMat<float> >(const CSRMat<float> &a)
{return s_cmplx(a,true);}
//re + i im: a BLAS axpy of the values if both have the same structure, otherwise a merge
template<>
const CSRMat<std::complex<double> > complexmatrix<CSRMat<double> >(const CSRMat<double> &re, const CSRMat<double> &im)
{CSRMat<std::complex<double> > r=s_cmplx(re,false); r.axpy(std::complex<double>(0,1),s_cmplx(im,false)); return r;}
template<>
const CSRMat<std::complex<float> > complexmatrix<CSRMat<float> >(const CSRMat<float> &re, const CSRMat<float> &im)
{CSRMat<std::complex<float> > r=s_cmplx(re,false); r.axpy(std::complex<float>(0,1),s_cmplx(im,false)); return r;}
/*
Commented out by Roman for ICC
#define INSTANTIZE(T) \ #define INSTANTIZE(T) \
template void CSRMat<T>::gemm(const T beta, const CSRMat &a, const char transa, const CSRMat &b, const char transb, const T alpha); \ template class CSRMat<T>; \
template CSRMat<T> & CSRMat<T>::operator*=(const T &a); \ template NRMat<T>::NRMat(const CSRMat<T> &rhs); \
template void CSRMat<T>::gemv(const T beta, NRVec<T> &r, const char trans, const T alpha, const NRVec<T> &x) const; \ template NRSMat<T>::NRSMat(const CSRMat<T> &rhs); \
template void CSRMat<T>::axpy(const T alpha, const CSRMat &x, const bool transp); \ template SparseMat<T>::SparseMat(const CSRMat<T> &rhs); \
template CSRMat<T> & CSRMat<T>::operator=(const T &a); \ template SparseSMat<T>::SparseSMat(const CSRMat<T> &rhs); \
template CSRMat<T> & CSRMat<T>::operator+=(const T &a); \ template void NRVec<T>::gemv(const T beta, const CSRMat<T> &a, const char trans, const T alpha, const NRVec<T> &x); \
template CSRMat<T> & CSRMat<T>::operator-=(const T &a); \ template NRVec<T>::NRVec(const CSRMat<T> &rhs); \
template LA_traits<T>::normtype CSRMat<T>::norm(const T scalar) const; \ template NRMat<T> & NRMat<T>::operator+=(const CSRMat<T> &rhs); \
template const T* CSRMat<T>::diagonalof(NRVec<T> &r, const bool divide, bool cache) const; \ template NRMat<T> & NRMat<T>::operator-=(const CSRMat<T> &rhs); \
template void CSRMat<T>::get(int fd, bool dimen, bool transp); \ template void NRMat<T>::gemm(const T &beta, const CSRMat<T> &a, const char transa, const NRMat<T> &b, const char transb, const T &alpha); \
template void CSRMat<T>::put(int fd, bool dimen, bool transp) const; \ template const NRMat<T> NRMat<T>::operator*(const CSRMat<T> &rhs) const; \
INSTANTIZE(double) INSTANTIZE(double)
INSTANTIZE(float)
INSTANTIZE(std::complex<double>) INSTANTIZE(std::complex<double>)
*/ INSTANTIZE(std::complex<float>)
//// forced instantization of functions in the header in the corresponding object file
//@@@@@template class CSRMat<double>;
//@@@@template class CSRMat<complex<double> >;
//@@@@ unfinished class commented out
}//namespace }//namespace
+142 -80
View File
@@ -1,6 +1,6 @@
/* /*
LA: linear algebra C++ interface library LA: linear algebra C++ interface library
Copyright (C) 2010 Jiri Pittner <jiri.pittner@jh-inst.cas.cz> or <jiri@pittnerovi.com> Copyright (C) 2010-2026 Jiri Pittner <jiri.pittner@jh-inst.cas.cz> or <jiri@pittnerovi.com>
This program is free software: you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by
@@ -32,16 +32,13 @@
#include "vec.h" #include "vec.h"
#include "mat.h" #include "mat.h"
#include "smat.h" #include "smat.h"
#include "qsort.h"
#ifdef CUDALA
#include "cusparse.h"
#include "cuda_runtime.h"
#endif
namespace LA { namespace LA {
//compressed row format read-efficient matrix storage //compressed row format read-efficient matrix storage
//row i has the elements v[k], column col[k], k=rowptr[i]..rowptr[i+1]-1, columns strictly ascending within a row
//the full matrix is always stored; symmetric only declares A = A^dagger (hermitian, real symmetric) e.g. for davidson, lanczos
//rowptr, col and v share data independently (copy-on-write), e.g. a scaled copy shares the structure with the original
template <typename T> template <typename T>
class CSRMat class CSRMat
@@ -49,92 +46,157 @@ class CSRMat
protected: protected:
SPMatindex nn; SPMatindex nn;
SPMatindex mm; SPMatindex mm;
SPMatindex nnz; bool symmetric;
int *count; NRVec<SPMatindex> rowptr;
SPMatindex *rowptr; NRVec<SPMatindex> col;
SPMatindex *col; NRVec<T> v;
T *v; void fromcoo(const SPMatindex n, const SPMatindex m, const int nz, const SPMatindex *ri, const SPMatindex *ci, const T *vi); //sorts, sums duplicates, drops small elements
#ifdef CUDALA void permrows(const SPMatindex *to); //row r becomes row to[r]
GPUID location;
cusparseMatDescr_t descr;
#endif
public: public:
CSRMat() : nn(0), mm(0), nnz(0), count(NULL), rowptr(NULL), col(NULL), v(NULL) {}; //iterator over the stored elements in row-major order
CSRMat(const CSRMat &rhs); class iterator {
explicit CSRMat(const SPMatindex n, const SPMatindex m); private:
explicit CSRMat(const SPMatindex n); const CSRMat *a;
SPMatindex row;
int k;
matel<T> my;
void set() {while(row<a->nn && k>=(int)a->rowptr[row+1]) ++row; if(row<a->nn) {my.row=row; my.col=a->col[k]; my.elem=a->v[k]; my.next=NULL;}}
public:
iterator(): a(NULL), row(0), k(0) {};
iterator(const CSRMat &rhs): a(&rhs), row(0), k(0) {if(a->nn) set();}
iterator & operator++() {++k; set(); return *this;}
bool notend() const {return a && row<a->nn;}
bool operator==(const iterator &rhs) const {return notend()==rhs.notend() && (!notend() || k==rhs.k);}
bool operator!=(const iterator &rhs) const {return !(*this==rhs);}
const matel<T> & operator*() const {return my;}
const matel<T> * operator->() const {return &my;}
};
iterator begin() const {return iterator(*this);}
iterator end() const {return iterator();}
CSRMat() : nn(0), mm(0), symmetric(false) {};
explicit CSRMat(const SPMatindex n, const SPMatindex m); //zero matrix
explicit CSRMat(const SPMatindex n); //zero square matrix
CSRMat(const SPMatindex n, const SPMatindex m, const NRVec<SPMatindex> &rowptr, const NRVec<SPMatindex> &col, const NRVec<T> &v); //shares the arrays
explicit CSRMat(const SparseMat<T> &rhs); explicit CSRMat(const SparseMat<T> &rhs);
explicit CSRMat(const SparseSMat<T> &rhs); explicit CSRMat(const SparseSMat<T> &rhs);
explicit CSRMat(const NRSMat<T> &rhs); explicit CSRMat(const NRSMat<T> &rhs);
explicit CSRMat(const NRMat<T> &rhs); explicit CSRMat(const NRMat<T> &rhs);
CSRMat & operator=(const CSRMat &rhs); void copyonwrite(bool detachonly=false, bool deep=true);
void copyonwrite(); void resize(const SPMatindex n, const SPMatindex m); //zero matrix
void resize(const SPMatindex nn, const SPMatindex mm);
void dealloc(void) {resize(0,0);} void dealloc(void) {resize(0,0);}
void moveto(GPUID destination); void clear() {bool sym=symmetric; resize(nn,mm); symmetric=sym;} //zero matrix of the same dimensions and symmetry
void clear(); void swap(CSRMat &rhs)
~CSRMat(); {
inline int getcount() const {return count?*count:0;} SPMatindex tmpnn=nn; nn=rhs.nn; rhs.nn=tmpnn;
inline const CSRMat operator*(const T &rhs) const {return CSRMat(*this) *= rhs;} SPMatindex tmpmm=mm; mm=rhs.mm; rhs.mm=tmpmm;
bool tmpsymmetric=symmetric; symmetric=rhs.symmetric; rhs.symmetric=tmpsymmetric;
rowptr.swap(rhs.rowptr); col.swap(rhs.col); v.swap(rhs.v);
}
inline SPMatindex nrows() const {return nn;}
inline SPMatindex ncols() const {return mm;}
inline int length() const {return v.size();} //number of stored elements
inline bool issymmetric() const {return symmetric;}
inline void setsymmetric() {if(nn!=mm) laerror("non-square cannot be symmetric"); symmetric=true;} //only declares the matrix hermitian
inline void setunsymmetric() {symmetric=false;}
inline const NRVec<SPMatindex> & getrowptr() const {return rowptr;}
inline const NRVec<SPMatindex> & getcol() const {return col;}
inline const NRVec<T> & getval() const {return v;}
const T operator()(const SPMatindex i, const SPMatindex j) const; //element (zero if not stored), binary search in the row
void gemv(const T beta, NRVec<T> &r, const char trans, const T alpha, const NRVec<T> &x) const {r.gemv(beta,*this,trans,alpha,x);};
inline const NRVec<T> operator*(const NRVec<T> &rhs) const {NRVec<T> result(nn); result.gemv((T)0,*this,'n',(T)1,rhs); return result;};
const T* diagonalof(NRVec<T> &r, const bool divide=0, bool cache=false) const; //diagonal (diagonal of A^T A for non-square, as SparseMat), or divide r by it
const CSRMat transpose(bool conj=false) const;
CSRMat & operator*=(const T &a); //multiply by a scalar (the structure stays shared)
CSRMat & operator=(const T &a); //a times the unit matrix
CSRMat & operator+=(const T &a); //add a to the diagonal
CSRMat & operator-=(const T &a) {return *this += -a;}
void identity() {*this = (T)1;}
void axpy(const T alpha, const CSRMat &x, const bool transp=false); //this += alpha x (alpha x^T); elements cancelled to zero stay stored, see simplify()
CSRMat & operator+=(const CSRMat &rhs) {axpy((T)1,rhs); return *this;}
CSRMat & operator-=(const CSRMat &rhs) {axpy((T)-1,rhs); return *this;}
inline const CSRMat operator*(const T &rhs) const {return CSRMat(*this) *= rhs;}
inline const CSRMat operator+(const T &rhs) const {return CSRMat(*this) += rhs;} inline const CSRMat operator+(const T &rhs) const {return CSRMat(*this) += rhs;}
inline const CSRMat operator-(const T &rhs) const {return CSRMat(*this) -= rhs;} inline const CSRMat operator-(const T &rhs) const {return CSRMat(*this) -= rhs;}
inline const CSRMat operator+(const CSRMat &rhs) const {return CSRMat(*this) += rhs;} inline const CSRMat operator+(const CSRMat &rhs) const {return CSRMat(*this) += rhs;}
inline const CSRMat operator-(const CSRMat &rhs) const {return CSRMat(*this) -= rhs;} inline const CSRMat operator-(const CSRMat &rhs) const {return CSRMat(*this) -= rhs;}
CSRMat & operator*=(const T &a); //multiply by a scalar const typename LA_traits<T>::normtype norm(const T scalar=(T)0) const; //Frobenius norm of A - scalar I
CSRMat & operator=(const T &a); //assign a to diagonal const T dot(const CSRMat &rhs) const; //\sum_{ij} conj(a_ij) b_ij as NRMat::dot
CSRMat & operator+=(const T &a); //assign a to diagonal const T dot(const NRMat<T> &rhs) const;
CSRMat & operator-=(const T &a); //assign a to diagonal const T trace() const;
void axpy(const T alpha, const CSRMat &x, const bool transp=0); // this+= a*x void simplify(const typename LA_traits<T>::normtype sparseepsilon=LA_precision<typename LA_traits<T>::normtype>::sparse_epsilon()); //removes elements with |x| <= sparseepsilon
inline CSRMat & operator+=(const CSRMat &rhs) {axpy((T)1,rhs); return *this;}; inline const NRMat<T> operator*(const NRMat<T> &rhs) const {NRMat<T> r(nn,rhs.ncols()); r.gemm((T)0,*this,'n',rhs,'n',(T)1); return r;};
inline CSRMat & operator-=(const CSRMat &rhs) {axpy((T)-1,rhs); return *this;}; void gemm(const T beta, const CSRMat &a, const char transa, const CSRMat &b, const char transb, const T alpha); //this = alpha op(A) op(B) + beta this, sparse result
const T* diagonalof(NRVec<T> &, const bool divide=0, bool cache=false) const; //get diagonal inline const CSRMat operator*(const CSRMat &rhs) const {CSRMat r; r.gemm((T)0,*this,'n',rhs,'n',(T)1); return r;};
void gemv(const T beta, NRVec<T> &r, const char trans, const T alpha, const NRVec<T> &x) const; CSRMat & oplusequal(const CSRMat &rhs); //direct sum
inline const NRVec<T> operator*(const NRVec<T> &rhs) const {NRVec<T> result(nn); this->gemv((T)0,result,'n',(T)1,rhs); return result;}; CSRMat & oplusequal(const NRMat<T> &rhs) {return oplusequal(CSRMat(rhs));}
typename LA_traits<T>::normtype norm(const T scalar=(T)0) const; CSRMat & oplusequal(const NRSMat<T> &rhs) {return oplusequal(CSRMat(rhs));}
inline const CSRMat operator*(const CSRMat &rhs) const {CSRMat<T> r(nn,mm); r.gemm(0,*this,'n',rhs,'n',1); return r;}; const CSRMat oplus(const CSRMat &rhs) const {return CSRMat(*this).oplusequal(rhs);}
void gemm(const T beta, const CSRMat &a, const char transa, const CSRMat &b, const char transb, const T alpha); //this := alpha*op( A )*op( B ) + beta*this const CSRMat oplus(const NRMat<T> &rhs) const {return CSRMat(*this).oplusequal(rhs);}
inline SPMatindex length() {return nnz;}; const CSRMat oplus(const NRSMat<T> &rhs) const {return CSRMat(*this).oplusequal(rhs);}
void transposeme() const {laerror("in-place transposition not implemented for CSRMat");}; const CSRMat otimes(const CSRMat &rhs) const; //direct product, (A x B)_{i nB+k, j mB+l} = a_ij b_kl as NRMat::operator|
CSRMat transpose(bool conj=false) const {laerror("CSR transpose not implemented");} const CSRMat otimes(const NRMat<T> &rhs) const {return otimes(CSRMat(rhs));}
void get(int fd, bool dimen, bool transp); const CSRMat otimes(const NRSMat<T> &rhs) const {return otimes(CSRMat(rhs));}
void put(int fd, bool dimen, bool transp) const; //permutations: element of row r goes to row p(r) (with inverse p^{-1}); matrix indices from 0, permutation from 1; the full matrix is stored, so rows means only rows
int nrows() const {return nn;} void permuteme_rows(const NRPerm<int> &p, const bool inverse=false);
int ncols() const {return mm;} void permuteme_cols(const NRPerm<int> &p, const bool inverse=false);
void permuteme_both(const NRPerm<int> &p, const NRPerm<int> &q, const bool inverse=false);
/*class iterator {//not efficient, just for output to ostreams const CSRMat permuted_rows(const NRPerm<int> &p, const bool inverse=false) const {CSRMat a(*this); a.permuteme_rows(p,inverse); return a;};
private: const CSRMat permuted_cols(const NRPerm<int> &p, const bool inverse=false) const {CSRMat a(*this); a.permuteme_cols(p,inverse); return a;};
matel<T> *p; const CSRMat permuted_both(const NRPerm<int> &p, const NRPerm<int> &q, const bool inverse=false) const {CSRMat a(*this); a.permuteme_both(p,q,inverse); return a;};
matel<T> my; void permuterows(const NRVec<SPMatindex> &p); //as SparseMat: row r goes to row p[r], indices from 0
SPMatindex mynn; void permutecolumns(const NRVec<SPMatindex> &p);
SPMatindex mymm; void permuteindices(const NRVec<SPMatindex> &p); //rows and columns
void transposeme(bool conj=false) {*this=transpose(conj);}
void get(int fd, bool dimensions=1, bool transp=0, bool orcaformat=false);
public: void put(int fd, bool dimensions=1, bool transp=0, bool orcaformat=false) const;
//compiler-generated iterator & operator=(const iterator &rhs);
//compiler-generated iterator(const iterator &rhs);
iterator()
iterator(const CSRMat &rhs)
iterator operator++()
iterator(matel<T> *q) {p=q; col=NULL;}//just for end()
//compiler-generated ~iterator() {};
bool operator!=(const iterator &rhs) const {return p!=rhs.p;} //only for comparison with end()
bool operator==(const iterator &rhs) const {return p==rhs.p;} //only for comparison with end()
matel<T> & operator*() const {return *p;}
matel<T> * operator->() const {return p;}
bool notend() const {return (bool)p;};
};
iterator begin() const {return iterator(*this);}
iterator end() const {return iterator(NULL);}
*/
}; };
template<typename T>
inline const CSRMat<T> commutator(const CSRMat<T> &x, const CSRMat<T> &y, const bool trx=0, const bool tryy=0)
{
CSRMat<T> r;
r.gemm((T)0,x,trx?'t':'n',y,tryy?'t':'n',(T)1);
r.gemm((T)1,y,tryy?'t':'n',x,trx?'t':'n',(T)-1);
return r;
}
template<typename T>
inline const CSRMat<T> anticommutator(const CSRMat<T> &x, const CSRMat<T> &y, const bool trx=0, const bool tryy=0)
{
CSRMat<T> r;
r.gemm((T)0,x,trx?'t':'n',y,tryy?'t':'n',(T)1);
r.gemm((T)1,y,tryy?'t':'n',x,trx?'t':'n',(T)1);
return r;
}
template <typename T> template <typename T>
std::ostream & operator<<(std::ostream &s, const CSRMat<T> &x); inline const NRVec<T> NRVec<T>::operator*(const CSRMat<T> &mat) const
{
NOT_GPU(*this);
NRVec<T> result(mat.ncols());
result.gemv((T)0,mat,'t',(T)1,*this);
return result;
}
template <class T> template <class T>
std::istream& operator>>(std::istream &s, CSRMat<T> &x); std::ostream& operator<<(std::ostream &s, const CSRMat<T> &x) //the same text format as SparseMat
{
s << (int)x.nrows() << ' ' << (int)x.ncols() << '\n';
for(typename CSRMat<T>::iterator p=x.begin(); p.notend(); ++p)
s << (int)p->row << ' ' << (int)p->col << ' ' << (typename LA_traits_io<T>::IOtype)p->elem << '\n';
s << "-1 -1\n";
return s;
}
template <class T>
std::istream& operator>>(std::istream &s, CSRMat<T> &x)
{
SparseMat<T> a;
s >> a;
x=CSRMat<T>(a);
return s;
}
}//namespace }//namespace
#endif //_CSRMAT_H_ #endif //_CSRMAT_H_
+2 -2
View File
@@ -47,7 +47,7 @@ namespace LA {
template <typename T, typename Matrix> template <typename T, typename Matrix>
void davidson(const Matrix &bigmat, NRVec<T> &eivals, NRVec<T> *eivecs, const char *eivecsfile, void davidson(const Matrix &bigmat, NRVec<T> &eivals, NRVec<T> *eivecs, const char *eivecsfile,
int nroots=1, const bool verbose=0, const double eps=1e-6, int nroots=1, const bool verbose=0, const double eps=LA_precision<typename LA_traits<T>::normtype>::eigensolver_eps(),
const bool incore=1, int maxit=100, const int maxkrylov = 500, const bool incore=1, int maxit=100, const int maxkrylov = 500,
void (*initguess)(NRVec<T> &)=NULL, const typename LA_traits<T>::normtype *target=NULL) void (*initguess)(NRVec<T> &)=NULL, const typename LA_traits<T>::normtype *target=NULL)
{ {
@@ -235,7 +235,7 @@ eival_n = r[nroot];
for(i=0; i<n; ++i) for(i=0; i<n; ++i)
{ {
typename LA_traits<T>::normtype denom = LA_traits<T>::realpart(diagonal[i]) - eival_n; typename LA_traits<T>::normtype denom = LA_traits<T>::realpart(diagonal[i]) - eival_n;
denom = denom<0?-std::max(0.1,std::abs(denom)):std::max(0.1,std::abs(denom)); denom = denom<0?-std::max((typename LA_traits<T>::normtype)0.1,std::abs(denom)):std::max((typename LA_traits<T>::normtype)0.1,std::abs(denom));
vec1[i] /= denom; vec1[i] /= denom;
} }
} }
+5
View File
@@ -0,0 +1,5 @@
%bibliography items of the chapter NRMat (mat.tex), cited in the comments of strassen.cc
\bibitem{mat:strassen1969} V. Strassen, Numer. Math. \textbf{13}, 354 (1969).
\bibitem{mat:winograd1971} S. Winograd, Linear Algebra Appl. \textbf{4}, 381 (1971).
\bibitem{mat:boyer2009} B. Boyer, J.-G. Dumas, C. Pernet, W. Zhou, in \emph{Proceedings of ISSAC 2009}, ACM, New York 2009, p.~55.
\bibitem{mat:husslederman1996} S. Huss-Lederman, E. M. Jacobson, J. R. Johnson, A. Tsao, T. Turnbull, in \emph{Proceedings of Supercomputing '96}, IEEE, 1996.
+2
View File
@@ -0,0 +1,2 @@
%bibliography items of the chapter Sparse matrices (sparse.tex), cited in the comments of csrmat.cc
\bibitem{sparse:gustavson1978} F. G. Gustavson, ACM Trans. Math. Softw. \textbf{4}, 250 (1978).
+10 -6
View File
@@ -5,7 +5,7 @@
\begin{itemize} \begin{itemize}
\item a C++11 capable compiler (the library is developed with GNU g++), \item a C++11 capable compiler (the library is developed with GNU g++),
\item BLAS and LAPACK (Fortran interface); optionally the C interfaces CBLAS and CLAPACK, \item BLAS and LAPACK (Fortran interface); optionally the C interfaces CBLAS and CLAPACK,
or Intel MKL, or ATLAS, or Intel MKL, or OpenBLAS, or ATLAS,
\item GNU autotools (autoconf, automake, libtool) when building from the git repository, \item GNU autotools (autoconf, automake, libtool) when building from the git repository,
\item optionally NVIDIA CUBLAS (experimental, section~\ref{sec:concepts:gpu}), \item optionally NVIDIA CUBLAS (experimental, section~\ref{sec:concepts:gpu}),
\item optionally doxygen for the generated source code reference, which is independent of this manual. \item optionally doxygen for the generated source code reference, which is independent of this manual.
@@ -16,7 +16,7 @@
\begin{lstlisting}[language=bash] \begin{lstlisting}[language=bash]
autoreconf --install # only if ./configure does not exist yet autoreconf --install # only if ./configure does not exist yet
./configure [options] CXXFLAGS=-I<path> LDFLAGS=-L<path> ./configure [options] CXXFLAGS=-I<path> LDFLAGS=-L<path>
make # library, and this manual and the doxygen pages if pdflatex/doxygen were found make # library, and this manual if pdflatex was found
make check # builds the test programs, runs testoptimize make check # builds the test programs, runs testoptimize
make install make install
\end{lstlisting} \end{lstlisting}
@@ -29,6 +29,10 @@ accordingly:
\begin{enumerate} \begin{enumerate}
\item Intel MKL (\code{mkl_intel_lp64}, \code{mkl_sequential}, \code{mkl_core} and the header \item Intel MKL (\code{mkl_intel_lp64}, \code{mkl_sequential}, \code{mkl_core} and the header
\header{mkl_cblas.h}); if found, \code{-DHAS_MKL -DNONCLAPACK} is used; \header{mkl_cblas.h}); if found, \code{-DHAS_MKL -DNONCLAPACK} is used;
\item otherwise OpenBLAS, if \code{libopenblas} contains LAPACK (\code{dgeev_}) and CBLAS (\code{cblas_dgemm}): only
\code{-lopenblas} is linked, \code{-DNONCLAPACK} is used and \code{-DNONCBLAS} if \header{cblas.h} is missing; skipped
with \code{--without-openblas} and with \code{--enable-fortran64int}. A threaded OpenBLAS uses all cores unless
\code{OPENBLAS_NUM_THREADS} is set;
\item otherwise ATLAS (optional) and the reference BLAS (mandatory), CBLAS (if \code{cblas_ddot} and \header{cblas.h} \item otherwise ATLAS (optional) and the reference BLAS (mandatory), CBLAS (if \code{cblas_ddot} and \header{cblas.h}
are not found, \code{-DNONCBLAS} is set) and LAPACK (mandatory; if the C interface \code{clapack_dgesv} are not found, \code{-DNONCBLAS} is set) and LAPACK (mandatory; if the C interface \code{clapack_dgesv}
with \header{clapack.h} is not found, \code{-DNONCLAPACK} is set); with \header{clapack.h} is not found, \code{-DNONCLAPACK} is set);
@@ -47,7 +51,8 @@ accordingly:
\code{--enable-optimize=<flags>} & compiler optimization flags, default \code{-O3 -finline-limit=1000}; \code{--disable-optimize} for none \\ \code{--enable-optimize=<flags>} & compiler optimization flags, default \code{-O3 -finline-limit=1000}; \code{--disable-optimize} for none \\
\code{--enable-profile} & compile with \code{-pg} \\ \code{--enable-profile} & compile with \code{-pg} \\
\code{--enable-matptr} & define \code{MATPTR}: \code{NRMat} stores an array of row pointers (\code{T**}), see chapter~\ref{ch:mat}; incompatible with CUDA \\ \code{--enable-matptr} & define \code{MATPTR}: \code{NRMat} stores an array of row pointers (\code{T**}), see chapter~\ref{ch:mat}; incompatible with CUDA \\
\code{--enable-fortran64int} & define \code{LONG_FORTRAN_INT} to link with BLAS and LAPACK using 64-bit integers; implies \code{NONCBLAS} and \code{NONCLAPACK} (the old misspelling \code{--enable-fotran64int} is accepted as an alias) \\ \code{--enable-fortran64int} & define \code{LONG_FORTRAN_INT} to link with BLAS and LAPACK using 64-bit integers; implies \code{NONCBLAS} and \code{NONCLAPACK} (the old misspelling \code{--enable-fotran64int} is accepted as an alias); OpenBLAS is then not used \\
\code{--without-openblas} & do not use OpenBLAS even if found (ATLAS or the reference BLAS is used) \\
\bottomrule \bottomrule
\end{longtable} \end{longtable}
@@ -72,7 +77,6 @@ functions do not match.
\code{LONG_FORTRAN_INT} & Fortran integers are 64-bit (\code{long}) \\ \code{LONG_FORTRAN_INT} & Fortran integers are 64-bit (\code{long}) \\
\code{MATPTR} & row pointer representation of \code{NRMat} \\ \code{MATPTR} & row pointer representation of \code{NRMat} \\
\code{CUDALA} & GPU support via CUBLAS (experimental) \\ \code{CUDALA} & GPU support via CUBLAS (experimental) \\
\code{NO_STRASSEN} & do not compile the experimental Strassen matrix multiplication interface (\header{strassen.cc}, needs an external library); always set in \header{Makefile.am} \\
\bottomrule \bottomrule
\end{longtable} \end{longtable}
@@ -117,5 +121,5 @@ This manual is in the subdirectory \header{doc}. \code{make manual} (alias \code
is done only when a \code{.tex} file is newer than the PDF, and a \LaTeX{} error stops the build with the end of the log. is done only when a \code{.tex} file is newer than the PDF, and a \LaTeX{} error stops the build with the end of the log.
A source code reference is generated by doxygen with the configuration file \header{doxygen.cfg} by \code{make doxygen} A source code reference is generated by doxygen with the configuration file \header{doxygen.cfg} by \code{make doxygen}
(output in \header{doxygen-doc}, rebuilt when a source file changes); it is independent of this manual. (output in \header{doxygen-doc}, rebuilt when a source file changes); it is independent of this manual.
\code{make} (i.e.\ \code{make all}) builds both in addition to the library, each only if \code{configure} found \code{make} (i.e.\ \code{make all}) builds the manual in addition to the library if \code{configure} found
\code{pdflatex} resp.\ \code{doxygen}; \code{make clean} removes \header{doc/.build} and \header{doxygen-doc} (not the PDF). \code{pdflatex}; the doxygen pages are built only by \code{make doxygen}. \code{make clean} removes \header{doc/.build} and \header{doxygen-doc} (not the PDF).
+28 -3
View File
@@ -8,7 +8,7 @@ it and describe only what is specific to the class.
\subsection{Shared data} \subsection{Shared data}
An object of \code{NRVec}, \code{NRMat}, \code{NRSMat} and of all other classes holding large data An object of \code{NRVec}, \code{NRMat}, \code{NRSMat} and of all other classes holding large data
(\code{SparseMat}, \code{SparseSMat}, \code{Tensor}, \code{fourindex}, \code{Polynomial}, the permutation (\code{SparseMat}, \code{SparseSMat}, \code{CSRMat}, \code{Tensor}, \code{fourindex}, \code{Polynomial}, the permutation
classes, \code{bitvector}, \dots) consists of a small header (dimensions, a pointer to the data and a pointer classes, \code{bitvector}, \dots) consists of a small header (dimensions, a pointer to the data and a pointer
to a reference counter) and the data block. The copy constructor and the assignment operator between objects to a reference counter) and the data block. The copy constructor and the assignment operator between objects
of the same type copy only the header and increment the reference counter, so that both objects share the of the same type copy only the header and increment the reference counter, so that both objects share the
@@ -145,6 +145,7 @@ Its main members are:
\lafunc{static void put(int fd, const T &x, ...); static void get(int fd, T &x, ...); multiput; multiget}{binary I/O, recursive for nested types} \lafunc{static void put(int fd, const T &x, ...); static void get(int fd, T &x, ...); multiput; multiget}{binary I/O, recursive for nested types}
\lafunc{static T conjugate(const T &x); realpart(x); imagpart(x); setrealpart(x,y); setimagpart(x,y)}{complex helpers, trivial for real types} \lafunc{static T conjugate(const T &x); realpart(x); imagpart(x); setrealpart(x,y); setimagpart(x,y)}{complex helpers, trivial for real types}
\lafunc{static bool bigger(const T &x, const T &y); smaller(x,y)}{comparison used for sorting; for complex types it is an error} \lafunc{static bool bigger(const T &x, const T &y); smaller(x,y)}{comparison used for sorting; for complex types it is an error}
\lafunc{static int strassen_cutoff()}{scalar types only: default \code{cutoff} of \code{NRMat::strassen} (section~\ref{sec:mat:strassen}), currently 2048 for all types}
\end{lareference} \end{lareference}
The auxiliary template \code{isscalar<T>} distinguishes scalars from classes, \code{LA_traits_complex<T>} The auxiliary template \code{isscalar<T>} distinguishes scalars from classes, \code{LA_traits_complex<T>}
gives for a complex type the component type and the corresponding real vector and matrix types (used by gives for a complex type the component type and the corresponding real vector and matrix types (used by
@@ -153,12 +154,36 @@ the mixed real--complex functions such as \code{gemv} of a complex vector with a
The functions \code{LA::real(c)} and \code{LA::imag(c)} return references to the real and imaginary part of a The functions \code{LA::real(c)} and \code{LA::imag(c)} return references to the real and imaginary part of a
\code{std::complex}, which can be used as l-values. \code{std::complex}, which can be used as l-values.
Default thresholds of the numerical methods depend on the floating point precision and are collected in
\code{LA_precision<T>} (\header{la_traits.h}), which is defined for \code{T = double} and \code{float} only (any other type
gives a compile-time error); functions with an argument of type \code{T} use \code{LA_precision<typename LA_traits<T>::normtype>},
so that complex types take the values of their real component. The double values are those used by the library before.
\begin{center}
\begin{tabular}{llll}
\toprule
member & double & float & used by \\
\midrule
\code{svd_threshold()} & $10^{-12}$ & $10^{-5}$ & \code{svd_solve}, \code{Tensor::Tucker} \\
\code{taylor_precision()} & $10^{-14}$ & $10^{-7}$ & \code{exp}, \code{sincos}, \code{exptimes} (\header{matexp.h}) \\
\code{conjgrad_tol()} & $10^{-8}$ & $10^{-5}$ & \code{conjgrad} \\
\code{gmres_eps()} & $10^{-7}$ & $10^{-5}$ & \code{gmres} \\
\code{eigensolver_eps()} & $10^{-6}$ & $10^{-4}$ & \code{davidson}, \code{lanczos} \\
\code{newton_threshold()} & $10^{-14}$ & $10^{-6}$ & \code{Polynomial::newton} \\
\code{det_tiny()} & $10^{-300}$ & $10^{-37}$ & underflow guard of the determinant in \code{linear_solve} \\
\code{sparse_epsilon()} & $10^{-15}$ & $10^{-7}$ & zero elements of \code{SparseMat}, \code{SparseSMat} (\code{SPARSEEPSILON}) \\
\code{cholesky_epsilon()} & $10^{-16}$ & $10^{-8}$ & pivots skipped by \code{SparseSMat::cholesky} (\code{CHOLESKYEPSILON}) \\
\bottomrule
\end{tabular}
\end{center}
\subsection{Element types and instantiation} \subsection{Element types and instantiation}
The class templates are explicitly instantiated in the library for \code{double}, \code{std::complex<double>} The class templates are explicitly instantiated in the library for \code{double}, \code{std::complex<double>}
and the integer types \code{char}, \code{short}, \code{int}, \code{long}, \code{long long} and their unsigned and the integer types \code{char}, \code{short}, \code{int}, \code{long}, \code{long long} and their unsigned
variants. The BLAS and LAPACK backed functions are implemented for \code{double} and \code{std::complex<double>} variants; \code{NRVec}, \code{NRMat}, \code{NRSMat}, \code{SparseMat}, \code{SparseSMat}, \code{Tensor}, \code{fourindex} (with \code{unsigned char} indices) and \code{Polynomial} also for \code{float} and
(some also for mixed real--complex arguments); calling them for other types results in a run-time \code{std::complex<float>} (single precision support, 2026, in progress for the other classes); \code{CSRMat} only for the four floating point types. The BLAS and LAPACK backed
functions are implemented for \code{double} and \code{std::complex<double>} (some also for mixed real--complex arguments)
and for their single precision counterparts, which always use the single precision BLAS/LAPACK routines (\code{s}/\code{c}); calling them for other types results in a run-time
\code{laerror} (``not implemented for this type'') or a link error. Functions implemented in the headers work \code{laerror} (``not implemented for this type'') or a link error. Functions implemented in the headers work
for any type with the necessary arithmetic operations. for any type with the necessary arithmetic operations.
+3 -3
View File
@@ -24,9 +24,9 @@ Indices are opaque to the sparse classes (they are never compared with the dimen
1-based indexing are possible there; the dense classes use indices from 1. 1-based indexing are possible there; the dense classes use indices from 1.
Only \code{fourindex<unsigned char,double>}, \code{fourindex_ext<unsigned char,double>} and Only \code{fourindex<unsigned char,double>}, \code{fourindex_ext<unsigned char,double>} and
\code{fourindex_dense<nosymmetry,double,unsigned char>} are explicitly instantiated in fourindex.cc; \code{fourindex_dense<nosymmetry,double,unsigned char>} and the same with \code{float} are explicitly instantiated in fourindex.cc;
everything else is implemented in the header and instantiated on use, except \code{fscanf()} and everything else is implemented in the header and instantiated on use, except \code{fscanf()} and
\code{fprintf()} which exist only for \code{fourindex<unsigned char,double>}. \code{fprintf()} which exist only for \code{fourindex<unsigned char,double>} and \code{fourindex<unsigned char,float>}.
\section{Symmetry types}\label{sec:fourindex:symmetry} \section{Symmetry types}\label{sec:fourindex:symmetry}
@@ -131,7 +131,7 @@ The symmetry must be set (not \code{undefined_symmetry}) for \code{piterator} an
\lafunc{unsigned long get(int fd, bool withattr=true)}{read the same format until end of file, \emph{adding} the elements to the list; returns their number.} \lafunc{unsigned long get(int fd, bool withattr=true)}{read the same format until end of file, \emph{adding} the elements to the list; returns their number.}
\lafunc{template <class I, class T> std::ostream& operator<<(std::ostream &s, const fourindex<I,T> &x)}{text output: the dimension, lines \code{i j k l value}, and a final line with four terminators and 0.} \lafunc{template <class I, class T> std::ostream& operator<<(std::ostream &s, const fourindex<I,T> &x)}{text output: the dimension, lines \code{i j k l value}, and a final line with four terminators and 0.}
\lafunc{template <class I, class T> std::istream& operator>>(std::istream &s, fourindex<I,T> &x)}{read the text format; \code{x} is resized to the read dimension (the symmetry is not set) and reading stops at the first line containing the terminator of \code{x} as an index.} \lafunc{template <class I, class T> std::istream& operator>>(std::istream &s, fourindex<I,T> &x)}{read the text format; \code{x} is resized to the read dimension (the symmetry is not set) and reading stops at the first line containing the terminator of \code{x} as an index.}
\lafunc{void fscanf(FILE *f); void fprintf(FILE *f, char *format) const}{C-style text I/O, only for \code{fourindex<unsigned char,double>}; line format \code{value i j k l}. \code{fscanf} adds elements until a line in which any index equals the terminator (compared after conversion to the index type, so $-1$ in the file matches the default terminator 255); \code{fprintf} applies \code{format} (which must consume a \code{double} and four \code{int}s) to each element and ends with a line \code{0 t t t t} with the terminator $t$. With terminator 0 this is the FCIDUMP-like convention (blocks separated by zero lines, read by consecutive \code{fscanf} calls; a one-electron line $i\,j\,0\,0$ also ends a block).} \lafunc{void fscanf(FILE *f); void fprintf(FILE *f, char *format) const}{C-style text I/O, only for \code{fourindex<unsigned char,double>} and \code{<unsigned char,float>}; line format \code{value i j k l}. \code{fscanf} adds elements until a line in which any index equals the terminator (compared after conversion to the index type, so $-1$ in the file matches the default terminator 255); \code{fprintf} applies \code{format} (which must consume a \code{double} and four \code{int}s) to each element and ends with a line \code{0 t t t t} with the terminator $t$. With terminator 0 this is the FCIDUMP-like convention (blocks separated by zero lines, read by consecutive \code{fscanf} calls; a one-electron line $i\,j\,0\,0$ also ends a block).}
\end{lareference} \end{lareference}
\section{Disc storage: \texttt{fourindex\_ext<I,T>}}\label{sec:fourindex:ext} \section{Disc storage: \texttt{fourindex\_ext<I,T>}}\label{sec:fourindex:ext}
+1 -1
View File
@@ -48,7 +48,7 @@ the Numerical Recipes library. They can be used with it, but are completely inde
\header{mat.h} & \code{NRMat<T>}, \code{NRMat_from1<T>} (chapter~\ref{ch:mat}) \\ \header{mat.h} & \code{NRMat<T>}, \code{NRMat_from1<T>} (chapter~\ref{ch:mat}) \\
\header{smat.h} & \code{NRSMat<T>}, \code{NRSMat_from1<T>} (chapter~\ref{ch:smat}) \\ \header{smat.h} & \code{NRSMat<T>}, \code{NRSMat_from1<T>} (chapter~\ref{ch:smat}) \\
\header{sparsemat.h}, \header{sparsesmat.h} & sparse matrices \code{SparseMat<T>}, \code{SparseSMat<T>} (chapter~\ref{ch:sparse}) \\ \header{sparsemat.h}, \header{sparsesmat.h} & sparse matrices \code{SparseMat<T>}, \code{SparseSMat<T>} (chapter~\ref{ch:sparse}) \\
\header{csrmat.h} & compressed row storage sparse matrix (unfinished) \\ \header{csrmat.h} & compressed row storage sparse matrix \code{CSRMat<T>} (chapter~\ref{ch:sparse}) \\
\header{nonclass.h}, \header{matexp.h} & linear systems, eigenvalue problems, SVD, matrix functions (chapter~\ref{ch:nonclass}) \\ \header{nonclass.h}, \header{matexp.h} & linear systems, eigenvalue problems, SVD, matrix functions (chapter~\ref{ch:nonclass}) \\
\header{davidson.h}, \header{lanczos.h}, \header{conjgrad.h}, \header{gmres.h}, \header{diis.h}, \header{bisection.h} & iterative methods, DIIS convergence acceleration (chapter~\ref{ch:iterative}) \\ \header{davidson.h}, \header{lanczos.h}, \header{conjgrad.h}, \header{gmres.h}, \header{diis.h}, \header{bisection.h} & iterative methods, DIIS convergence acceleration (chapter~\ref{ch:iterative}) \\
\header{optimize.h} & numerical optimization (chapter~\ref{ch:optimize}) \\ \header{optimize.h} & numerical optimization (chapter~\ref{ch:optimize}) \\
+7 -4
View File
@@ -14,6 +14,9 @@ or of the normal equations $A\Tr Ax=A\Tr b$ for rectangular $A$ (least squares);
\item the class \code{DIIS} (\header{diis.h}): Pulay's DIIS convergence acceleration of a general fixed-point iteration; \item the class \code{DIIS} (\header{diis.h}): Pulay's DIIS convergence acceleration of a general fixed-point iteration;
\item \code{bisection_find()} and \code{interpolation_find()} (\header{bisection.h}): search in a sorted array. \item \code{bisection_find()} and \code{interpolation_find()} (\header{bisection.h}): search in a sorted array.
\end{itemize} \end{itemize}
All of them work for \code{T = float} and \code{std::complex<float>} as well (where the double versions support the
corresponding real or complex type); their default thresholds then come from \code{LA_precision<float>}
(section~\ref{sec:concepts:traits}).
Dense (LAPACK based) diagonalization, linear solvers and matrix functions are in chapter~\ref{ch:nonclass}; the small Dense (LAPACK based) diagonalization, linear solvers and matrix functions are in chapter~\ref{ch:nonclass}; the small
subspace problems of \code{davidson} and \code{lanczos} are solved by the routines described there subspace problems of \code{davidson} and \code{lanczos} are solved by the routines described there
(\code{diagonalize}, \code{gdiagonalize}, \code{diagonalize3}, \code{linear_solve}). The quasi-Newton GDIIS optimizer built on (\code{diagonalize}, \code{gdiagonalize}, \code{diagonalize3}, \code{linear_solve}). The quasi-Newton GDIIS optimizer built on
@@ -96,7 +99,7 @@ problem.
\laheader{davidson.h} \laheader{davidson.h}
\begin{lareference} \begin{lareference}
\lafunc{template <typename T, typename Matrix> void davidson(const Matrix &bigmat, NRVec<T> &eivals, NRVec<T> *eivecs, const char *eivecsfile, int nroots=1, const bool verbose=0, const double eps=1e-6, const bool incore=1, int maxit=100, const int maxkrylov = 500, void (*initguess)(NRVec<T> &)=NULL, const typename LA_traits<T>::normtype *target=NULL)}{ \lafunc{template <typename T, typename Matrix> void davidson(const Matrix &bigmat, NRVec<T> &eivals, NRVec<T> *eivecs, const char *eivecsfile, int nroots=1, const bool verbose=0, const double eps=LA_precision<normtype>::eigensolver_eps(), const bool incore=1, int maxit=100, const int maxkrylov = 500, void (*initguess)(NRVec<T> &)=NULL, const typename LA_traits<T>::normtype *target=NULL)}{
Davidson's method \cite{iterative:davidson1975} for the \code{nroots} lowest eigenvalues of \code{bigmat}. Davidson's method \cite{iterative:davidson1975} for the \code{nroots} lowest eigenvalues of \code{bigmat}.
\begin{itemize} \begin{itemize}
\item \code{eivals}: on return the eigenvalues in its first \code{nroots} elements (ascending, or by increasing distance from \item \code{eivals}: on return the eigenvalues in its first \code{nroots} elements (ascending, or by increasing distance from
@@ -167,7 +170,7 @@ implicitly given one, built column by column by \code{gemv} on unit vectors (for
\laheader{lanczos.h} \laheader{lanczos.h}
\begin{lareference} \begin{lareference}
\lafunc{template <typename T, typename Matrix> void lanczos(const Matrix &bigmat, NRVec<T> &eivals, NRVec<T> *eivecs, const char *eivecsfile, int nroots=1, const bool verbose=0, const double eps=1e-6, const bool incore=1, int maxit=100, const int maxkrylov = 1000, void (*initguess)(NRVec<T> &)=NULL, const typename LA_traits<T>::normtype *target=NULL)}{ \lafunc{template <typename T, typename Matrix> void lanczos(const Matrix &bigmat, NRVec<T> &eivals, NRVec<T> *eivecs, const char *eivecsfile, int nroots=1, const bool verbose=0, const double eps=LA_precision<normtype>::eigensolver_eps(), const bool incore=1, int maxit=100, const int maxkrylov = 1000, void (*initguess)(NRVec<T> &)=NULL, const typename LA_traits<T>::normtype *target=NULL)}{
Lanczos method for the \code{nroots} lowest (or closest to \code{*target}) eigenvalues of a hermitian matrix. The parameters Lanczos method for the \code{nroots} lowest (or closest to \code{*target}) eigenvalues of a hermitian matrix. The parameters
have the same meaning as for \code{davidson}, with these differences: \code{bigmat.issymmetric()} must be \code{true} have the same meaning as for \code{davidson}, with these differences: \code{bigmat.issymmetric()} must be \code{true}
(\code{laerror} otherwise); the start vector is normalized by the function; \code{incore=false} keeps only the Lanczos vectors (\code{laerror} otherwise); the start vector is normalized by the function; \code{incore=false} keeps only the Lanczos vectors
@@ -194,7 +197,7 @@ the method fails with \code{laerror} when more than \code{maxit} iterations are
\laheader{conjgrad.h} \laheader{conjgrad.h}
\begin{lareference} \begin{lareference}
\lafunc{template<typename T, typename Matrix> bool conjgrad(const Matrix &bigmat, const NRVec<T> &b, NRVec<T> &x, const bool doguess=true, const double tol=1e-8, const int itmax=1000, const bool verbose=true, bool issquare=1, const bool precondition=1)}{ \lafunc{template<typename T, typename Matrix> bool conjgrad(const Matrix &bigmat, const NRVec<T> &b, NRVec<T> &x, const bool doguess=true, const double tol=LA_precision<normtype>::conjgrad_tol(), const int itmax=1000, const bool verbose=true, bool issquare=1, const bool precondition=1)}{
Solves $Ax=b$ by the (Jacobi preconditioned) conjugate gradient method (the source refers to the book of Stoer and Bulirsch). Solves $Ax=b$ by the (Jacobi preconditioned) conjugate gradient method (the source refers to the book of Stoer and Bulirsch).
Returns \code{true} if converged, \code{false} after \code{itmax}+1 iterations without convergence; \code{x} then holds the Returns \code{true} if converged, \code{false} after \code{itmax}+1 iterations without convergence; \code{x} then holds the
last iterate. last iterate.
@@ -227,7 +230,7 @@ $p\cdot Ap$ (or $|Ap|^2$) or the residual product $r\cdot z$ vanishes. Only real
\laheader{gmres.h} \laheader{gmres.h}
\begin{lareference} \begin{lareference}
\lafunc{template<typename T, typename Matrix> bool gmres(const Matrix &bigmat, const NRVec<T> &b, NRVec<T> &x, const bool doguess=1, const double eps=1e-7, const int MAXIT=50, const bool verbose=1, bool square=1, const bool precondition=1, int neustart=0, const int incore=1)}{ \lafunc{template<typename T, typename Matrix> bool gmres(const Matrix &bigmat, const NRVec<T> &b, NRVec<T> &x, const bool doguess=1, const double eps=LA_precision<normtype>::gmres_eps(), const int MAXIT=50, const bool verbose=1, bool square=1, const bool precondition=1, int neustart=0, const int incore=1)}{
Solves $Ax=b$ by restarted GMRES with Givens rotations (after H.~R.~Schwarz, original implementation by M.~Warken). Returns Solves $Ax=b$ by restarted GMRES with Givens rotations (after H.~R.~Schwarz, original implementation by M.~Warken). Returns
\code{true} if converged; otherwise \code{false}, with \code{x} holding the last iterate. \code{true} if converged; otherwise \code{false}, with \code{x} holding the last iterate.
\begin{itemize} \begin{itemize}
+35 -24
View File
@@ -30,9 +30,9 @@ reference count is larger than one (controlled by \code{_LA_count_check}); read
possibly shared matrix through a const reference. possibly shared matrix through a const reference.
\paragraph{Element types.} \code{mat.cc} explicitly instantiates \code{NRMat<T>} for \paragraph{Element types.} \code{mat.cc} explicitly instantiates \code{NRMat<T>} for
\code{double}, \code{std::complex<double>}, and the integer types \code{char}, \code{short}, \code{int}, \code{double}, \code{float}, \code{std::complex<double>}, \code{std::complex<float>}, and the integer types \code{char}, \code{short}, \code{int},
\code{long}, \code{long long} and their unsigned variants. Many numerical members are only \code{long}, \code{long long} and their unsigned variants. Many numerical members are only
specialized (BLAS-backed) for \code{double} and \code{complex<double>} and have no generic specialized (BLAS-backed) for the four floating point types and have no generic
definition, so using them with an integer type results in a link error. Table~\ref{tab:mat:types} definition, so using them with an integer type results in a link error. Table~\ref{tab:mat:types}
summarizes this. Nested element types (e.g.\ \code{NRMat<NRVec<double> >}) are supported by the summarizes this. Nested element types (e.g.\ \code{NRMat<NRVec<double> >}) are supported by the
reference counting (type-nested \code{copyonwrite}, sec.~\ref{sec:concepts:traits}), but several reference counting (type-nested \code{copyonwrite}, sec.~\ref{sec:concepts:traits}), but several
@@ -47,10 +47,10 @@ Members & Available for \\
\midrule \midrule
\texttt{gemm}, \texttt{norm}, \texttt{axpy(T,const NRMat\&)}, \texttt{dot}, \texttt{transpose}, \texttt{diagmultl}, \texttt{diagmultr}, \texttt{gemm}, \texttt{norm}, \texttt{axpy(T,const NRMat\&)}, \texttt{dot}, \texttt{transpose}, \texttt{diagmultl}, \texttt{diagmultr},
\texttt{operator*(const NRSMat\&)}, \texttt{diagonalset}, \texttt{inverse}, \texttt{svdinverse}, \texttt{absadjoint}, \texttt{operator*(const NRSMat\&)}, \texttt{diagonalset}, \texttt{inverse}, \texttt{svdinverse}, \texttt{absadjoint},
\texttt{randomize}, \texttt{amax}, \texttt{amin} & \texttt{double}, \texttt{complex<double>} \\ \texttt{randomize}, \texttt{amax}, \texttt{amin}, \texttt{strassen} & \texttt{double}, \texttt{float}, \texttt{complex<double>}, \texttt{complex<float>} \\
\texttt{operator*(const NRMat\&)} & \texttt{double}, \texttt{complex<double>}, \texttt{int} \\ \texttt{operator*(const NRMat\&)} & the same and \texttt{int} \\
\texttt{diagonalof}, \texttt{orthonormalize}, \texttt{strassen}, \texttt{gemm} with \texttt{SparseMat} & \texttt{double} only \\ \texttt{diagonalof}, \texttt{orthonormalize}, \texttt{gemm} with \texttt{SparseMat} & \texttt{double}, \texttt{float} \\
\texttt{nonhermiticity} & \texttt{complex<double>} only \\ \texttt{nonhermiticity} & \texttt{complex<double>}, \texttt{complex<float>} \\
everything else & all instantiated types \\ everything else & all instantiated types \\
\bottomrule \bottomrule
\end{tabular} \end{tabular}
@@ -73,14 +73,23 @@ MATPTR is rarely used and less tested than the default representation; the libra
regression test (\code{testoptimize}). regression test (\code{testoptimize}).
\paragraph{Strassen multiplication.}\label{sec:mat:strassen} \paragraph{Strassen multiplication.}\label{sec:mat:strassen}
Unless \code{NO_STRASSEN} is defined, \code{NRMat} declares \code{strassen()} and \code{s_cutoff()} \code{strassen()} (in \code{strassen.cc}, for \code{double}, \code{float}, \code{complex<double>} and \code{complex<float>}) computes the
(specialized for \code{double} only, in \code{strassen.cc}). They call the external Fortran-compatible same $C=\beta C+\alpha\,\mathrm{op}(A)\,\mathrm{op}(B)$ as \code{gemm} with the same parameters, plus an optional \code{cutoff}, by the
routines \code{fmm} and \code{strassen_cutoff}, which are \emph{not} part of \LA{} algorithm of Strassen \cite{mat:strassen1969} in the variant of Winograd (7 products and 15 additions of quadrants per level)
(the test code in \code{t.cc} mentions linking \code{-lstrassen}). Strassen multiplication is never \cite{mat:winograd1971}, which needs $O(n^{\log_2 7})=O(n^{2.81})$ operations. The recursion works on strided quadrants of the original
used implicitly: \code{operator*} and \code{gemm} always call BLAS \code{gemm}. Note that matrices (no copies of $A$ and $B$, transpositions are passed to the products), the products of the blocks are computed by BLAS
\code{Makefile.am} unconditionally adds \code{-DNO_STRASSEN}, so in the standard build these members do not exist. \code{gemm}, the order of operations follows the memory-saving schedule with two temporaries per level of \cite{mat:boyer2009} and odd
\lawarning{The Strassen interface is \emph{experimental}: it has not been used or tested for a long time. It is kept for possible future work, dimensions are treated by dynamic peeling \cite{mat:husslederman1996}. A level of recursion is done only while all of $m$, $n$, $k$ are larger
in particular if fast (sub-$O(n^3)$) matrix multiplication algorithms become practically useful.} than \code{cutoff} (default \code{LA_traits<T>::strassen_cutoff()}, 2048 for all types), so products with a dimension $\le$
\code{cutoff} are plain \code{gemm} calls. Workspace of about $\frac23 mn$ elements is allocated (plus $mn$ if $\beta\ne0$).
Strassen multiplication is never used implicitly: \code{operator*} and \code{gemm} always use the classical algorithm.
Whether it pays off depends on the BLAS and the machine: on top of an optimized multithreaded BLAS the saving of one product in eight
is largely eaten by the memory-bound additions, so a gain is to be expected mainly for large matrices ($n\gtrsim4000$) and single-threaded
BLAS; the cutoff should be tuned by a benchmark. CPU only (\code{laerror} for matrices on the GPU); the device-dependent operations
are separated in \code{strassen.cc} for a future CUDA version.
\lawarning{The error of Strassen multiplication is bounded only normwise, not componentwise as for the classical algorithm, and the bound
grows by roughly a factor of 12 per level of recursion. Small elements of the result may therefore be less accurate than with \code{gemm}.}
\section{Construction, assignment, conversion}\label{sec:mat:constr} \section{Construction, assignment, conversion}\label{sec:mat:constr}
\begin{lareference} \begin{lareference}
@@ -93,7 +102,7 @@ in particular if fast (sub-$O(n^3)$) matrix multiplication algorithms become pra
\lafunc{explicit NRMat(const int n) = delete}{A single integer argument is forbidden (it would otherwise silently create a $3\times3$ matrix \lafunc{explicit NRMat(const int n) = delete}{A single integer argument is forbidden (it would otherwise silently create a $3\times3$ matrix
through \code{Mat3}); use \code{NRMat(n,m)}.} through \code{Mat3}); use \code{NRMat(n,m)}.}
\lafunc{NRMat(const NRMat &rhs)}{Shallow copy (shares data).} \lafunc{NRMat(const NRMat &rhs)}{Shallow copy (shares data).}
\lafunc{NRMat(const typename LA_traits_complex<T>::NRMat_Noncomplex_type &rhs, bool imagpart = false)}{Complexifying (non-explicit) constructor, defined for \code{T = complex<double>}: \code{rhs} (\code{NRMat<double>}) becomes the real part (or the imaginary part if \code{imagpart}), the other part is zero.} \lafunc{NRMat(const typename LA_traits_complex<T>::NRMat_Noncomplex_type &rhs, bool imagpart = false)}{Complexifying (non-explicit) constructor, defined for \code{T = complex<double>} and \code{complex<float>}: \code{rhs} (\code{NRMat<double>} resp.\ \code{NRMat<float>}) becomes the real part (or the imaginary part if \code{imagpart}), the other part is zero.}
\lafunc{explicit NRMat(const NRMat<std::complex<T> > &rhs, bool imagpart = false)}{Decomplexifying constructor, defined for \code{T = double}: real part (or imaginary part if \code{imagpart}) of \code{rhs}.} \lafunc{explicit NRMat(const NRMat<std::complex<T> > &rhs, bool imagpart = false)}{Decomplexifying constructor, defined for \code{T = double}: real part (or imaginary part if \code{imagpart}) of \code{rhs}.}
\lafunc{explicit NRMat(const NRSMat<T> &rhs)}{Full square matrix from a packed symmetric one ($A_{ij}=A_{ji}$; for complex types hermitian, $A_{ij}=A_{ji}^*$).} \lafunc{explicit NRMat(const NRSMat<T> &rhs)}{Full square matrix from a packed symmetric one ($A_{ij}=A_{ji}$; for complex types hermitian, $A_{ij}=A_{ji}^*$).}
\lafunc{explicit NRMat(const NRVec<T> &rhs, const int n, const int m, const int offset = 0)}{View of the vector data from position \code{offset} as an $n\times m$ matrix. Without MATPTR the data are \emph{shared} (reference count of the vector is incremented); with MATPTR they are copied. A nonzero offset gives a view into the middle of the vector without copying (a deliberate feature); \lafunc{explicit NRMat(const NRVec<T> &rhs, const int n, const int m, const int offset = 0)}{View of the vector data from position \code{offset} as an $n\times m$ matrix. Without MATPTR the data are \emph{shared} (reference count of the vector is incremented); with MATPTR they are copied. A nonzero offset gives a view into the middle of the vector without copying (a deliberate feature);
@@ -102,7 +111,7 @@ its condition of use is that the vector outlives the matrix, see Pitfalls. The c
\lafunc{explicit NRMat(const WeightPermutation<int,T> &p, const bool direction)}{Weight times permutation matrix.} \lafunc{explicit NRMat(const WeightPermutation<int,T> &p, const bool direction)}{Weight times permutation matrix.}
\lafunc{explicit NRMat(const PermutationAlgebra<int,T> &p, const bool direction, const int nforce = 0)}{Sum of weighted permutation matrices of the elements of a permutation algebra; size taken from the first element unless \code{nforce>0}. (The header remarks that the permutation-matrix representation of $S_n$ is reducible to only the irreps $[n]$ and $[n-1,1]$, so e.g.\ Young projectors cannot be represented this way.) See chapter~\ref{ch:permutation}.} \lafunc{explicit NRMat(const PermutationAlgebra<int,T> &p, const bool direction, const int nforce = 0)}{Sum of weighted permutation matrices of the elements of a permutation algebra; size taken from the first element unless \code{nforce>0}. (The header remarks that the permutation-matrix representation of $S_n$ is reducible to only the irreps $[n]$ and $[n-1,1]$, so e.g.\ Young projectors cannot be represented this way.) See chapter~\ref{ch:permutation}.}
\lafunc{explicit NRMat(const SparseMat<T> &rhs); explicit NRMat(const SparseSMat<T> &rhs); explicit NRMat(const SemiSparseMat<T> &rhs)}{Dense matrix from sparse ones (chapter~\ref{ch:sparse}).} \lafunc{explicit NRMat(const SparseMat<T> &rhs); explicit NRMat(const SparseSMat<T> &rhs); explicit NRMat(const SemiSparseMat<T> &rhs)}{Dense matrix from sparse ones (chapter~\ref{ch:sparse}).}
\lafunc{explicit NRMat(const CSRMat<T> &rhs)}{Not supported: \code{CSRMat} is unfinished, the declaration is kept for possible future work (no definition, a link error if used).} \lafunc{explicit NRMat(const CSRMat<T> &rhs)}{Dense matrix from a compressed row sparse one (section~\ref{sec:sparse:csr}).}
\lafunc{~NRMat()}{Decrements the reference count, frees the data when it reaches zero.} \lafunc{~NRMat()}{Decrements the reference count, frees the data when it reaches zero.}
\lafunc{NRMat & operator=(const NRMat &rhs)}{Shallow assignment (sec.~\ref{sec:concepts:assign}).} \lafunc{NRMat & operator=(const NRMat &rhs)}{Shallow assignment (sec.~\ref{sec:concepts:assign}).}
\lafunc{NRMat & operator|=(const NRMat &rhs)}{Deep copy.} \lafunc{NRMat & operator|=(const NRMat &rhs)}{Deep copy.}
@@ -140,10 +149,11 @@ Note that scalar addition and subtraction act on the \emph{diagonal} only, i.e.\
\lafunc{NRMat & operator^=(const NRMat<T> &rhs)}{Hadamard (element-wise) product $A_{ij}\leftarrow A_{ij}B_{ij}$.} \lafunc{NRMat & operator^=(const NRMat<T> &rhs)}{Hadamard (element-wise) product $A_{ij}\leftarrow A_{ij}B_{ij}$.}
\lafunc{void axpy(const T alpha, const NRMat &x)}{$A\leftarrow A+\alpha X$ (BLAS \code{axpy}).} \lafunc{void axpy(const T alpha, const NRMat &x)}{$A\leftarrow A+\alpha X$ (BLAS \code{axpy}).}
\lafunc{const NRMat operator*(const NRMat &rhs) const}{Matrix product $AB$ (BLAS \code{gemm}; simple loops for \code{int}).} \lafunc{const NRMat operator*(const NRMat &rhs) const}{Matrix product $AB$ (BLAS \code{gemm}; simple loops for \code{int}).}
\lafunc{void gemm(const T &beta, const NRMat &a, const char transa, const NRMat &b, const char transb, const T &alpha)}{$C\leftarrow\alpha\,\mathrm{op}(A)\,\mathrm{op}(B)+\beta C$ with $C$ = \code{*this}, which must already have the correct dimensions (it is not resized; if $\beta=0$ its contents may be uninitialized). \code{trans}: \code{'n'} no transposition; for \code{double} any other character means transposition; for \code{complex<double>} \code{'t'} transposition and \code{'c'} conjugate transposition (case insensitive). Returns immediately when $\alpha=0$ and $\beta=1$. \code{*this} must not share storage with \code{a} or \code{b}.} \lafunc{void gemm(const T &beta, const NRMat &a, const char transa, const NRMat &b, const char transb, const T &alpha)}{$C\leftarrow\alpha\,\mathrm{op}(A)\,\mathrm{op}(B)+\beta C$ with $C$ = \code{*this}, which must already have the correct dimensions (it is not resized; if $\beta=0$ its contents may be uninitialized). \code{trans}: \code{'n'} no transposition; for real types any other character means transposition; for complex types \code{'t'} transposition and \code{'c'} conjugate transposition (case insensitive). Returns immediately when $\alpha=0$ and $\beta=1$. \code{*this} must not share storage with \code{a} or \code{b}.}
\lafunc{void gemm(const T &beta, const SparseMat<T> &a, const char transa, const NRMat &b, const char transb, const T &alpha)}{The same with a sparse first factor; \code{double} only (the complex version calls \code{laerror("not implemented yet")}), see chapter~\ref{ch:sparse}.} \lafunc{void gemm(const T &beta, const SparseMat<T> &a, const char transa, const NRMat &b, const char transb, const T &alpha)}{The same with a sparse first factor; \code{double} and \code{float} only (the complex versions call \code{laerror("not implemented yet")}), see chapter~\ref{ch:sparse}.}
\lafunc{const NRMat operator*(const NRSMat<T> &rhs) const}{$AS$ with packed symmetric (real) or hermitian (complex) $S$ (BLAS \code{dspmv}/\code{zhpmv} per row).} \lafunc{const NRMat operator*(const NRSMat<T> &rhs) const}{$AS$ with packed symmetric (real) or hermitian (complex) $S$ (BLAS \code{dspmv}/\code{zhpmv} per row).}
\lafunc{SparseSMat<T> operator*(const SparseSMat<T> &rhs) const}{Dense times sparse, the result is sparse (chapter~\ref{ch:sparse}).} \lafunc{SparseSMat<T> operator*(const SparseSMat<T> &rhs) const}{Dense times sparse, the result is sparse (chapter~\ref{ch:sparse}).}
\lafunc{void gemm(const T &beta, const CSRMat<T> &a, const char transa, const NRMat &b, const char transb, const T &alpha); const NRMat operator*(const CSRMat<T> &rhs) const}{$C\leftarrow\beta C+\alpha\,\mathrm{op}(A)\,\mathrm{op}(B)$ with a compressed row sparse $A$ (all types, \code{trans} \code{'n'}, \code{'t'}, \code{'c'}), and dense times compressed row sparse with a dense result (section~\ref{sec:sparse:csr}).}
\lafunc{const NRVec<T> operator*(const NRVec<T> &rhs) const; const NRVec<std::complex<T> > operator*(const NRVec<std::complex<T> > &rhs) const}{Matrix--vector product via \code{NRVec::gemv} (the second form: real matrix times complex vector).} \lafunc{const NRVec<T> operator*(const NRVec<T> &rhs) const; const NRVec<std::complex<T> > operator*(const NRVec<std::complex<T> > &rhs) const}{Matrix--vector product via \code{NRVec::gemv} (the second form: real matrix times complex vector).}
\lafunc{void gemv(const T beta, NRVec<T> &r, const char trans, const T alpha, const NRVec<T> &x) const; void gemv(const T beta, NRVec<std::complex<T> > &r, const char trans, const T alpha, const NRVec<std::complex<T> > &x) const}{$r\leftarrow\alpha\,\mathrm{op}(A)x+\beta r$; forwards to \code{r.gemv(beta,*this,trans,alpha,x)}, see chapter~\ref{ch:vec}.} \lafunc{void gemv(const T beta, NRVec<T> &r, const char trans, const T alpha, const NRVec<T> &x) const; void gemv(const T beta, NRVec<std::complex<T> > &r, const char trans, const T alpha, const NRVec<std::complex<T> > &x) const}{$r\leftarrow\alpha\,\mathrm{op}(A)x+\beta r$; forwards to \code{r.gemv(beta,*this,trans,alpha,x)}, see chapter~\ref{ch:vec}.}
\lafunc{const T dot(const NRMat &rhs) const}{$\sum_{ij}A_{ij}^*B_{ij}$ (Frobenius inner product, conjugating \code{*this}).} \lafunc{const T dot(const NRMat &rhs) const}{$\sum_{ij}A_{ij}^*B_{ij}$ (Frobenius inner product, conjugating \code{*this}).}
@@ -153,7 +163,7 @@ Note that scalar addition and subtraction act on the \emph{diagonal} only, i.e.\
\lafunc{const NRMat operator&(const NRMat &rhs) const; const NRMat oplus(const NRMat &rhs) const}{Direct sum $A\oplus B$ (block-diagonal matrix). \code{oplus} returns the other operand when one is empty; \code{operator&} is plain-data only.} \lafunc{const NRMat operator&(const NRMat &rhs) const; const NRMat oplus(const NRMat &rhs) const}{Direct sum $A\oplus B$ (block-diagonal matrix). \code{oplus} returns the other operand when one is empty; \code{operator&} is plain-data only.}
\lafunc{const NRMat operator|(const NRMat<T> &rhs) const}{Direct (Kronecker) product $A\otimes B$, $(A\otimes B)_{i n_B+k,\,j m_B+l}=A_{ij}B_{kl}$.} \lafunc{const NRMat operator|(const NRMat<T> &rhs) const}{Direct (Kronecker) product $A\otimes B$, $(A\otimes B)_{i n_B+k,\,j m_B+l}=A_{ij}B_{kl}$.}
\lafunc{const NRMat otimes(const NRMat &rhs, bool reversecolumns = false) const}{Kronecker product as \code{operator|}; with \code{reversecolumns} the column index is ordered the other way, $R_{i n_B+k,\,l m_A+j}=A_{ij}B_{kl}$. Returns the empty operand when one is empty.} \lafunc{const NRMat otimes(const NRMat &rhs, bool reversecolumns = false) const}{Kronecker product as \code{operator|}; with \code{reversecolumns} the column index is ordered the other way, $R_{i n_B+k,\,l m_A+j}=A_{ij}B_{kl}$. Returns the empty operand when one is empty.}
\lafunc{void strassen(const T beta, const NRMat &a, const char transa, const NRMat &b, const char transb, const T alpha); void s_cutoff(const int, const int, const int, const int) const}{Only without \code{NO_STRASSEN}, \code{double} only: Strassen multiplication with \code{gemm} semantics (dimensions always checked) and setting of the cutoffs of the external Strassen library (section~\ref{sec:mat:strassen}).} \lafunc{void strassen(const T &beta, const NRMat &a, const char transa, const NRMat &b, const char transb, const T &alpha, const int cutoff=LA_traits<T>::strassen_cutoff())}{Strassen-Winograd multiplication with the parameters and semantics of \code{gemm}; blocks with a dimension $\le$ \code{cutoff} are multiplied by \code{gemm} (section~\ref{sec:mat:strassen}).}
\end{lareference} \end{lareference}
\section{Other member functions}\label{sec:mat:other} \section{Other member functions}\label{sec:mat:other}
@@ -167,13 +177,13 @@ Note that scalar addition and subtraction act on the \emph{diagonal} only, i.e.\
\lafunc{const NRMat submatrix(const NRVec<int> &rows, const NRVec<int> &cols) const}{Copy of the elements $A_{r_i c_j}$ for given (arbitrary) lists of row and column indices; indices are always range-checked.} \lafunc{const NRMat submatrix(const NRVec<int> &rows, const NRVec<int> &cols) const}{Copy of the elements $A_{r_i c_j}$ for given (arbitrary) lists of row and column indices; indices are always range-checked.}
\lafunc{void storesubmatrix(const int fromrow, const int fromcol, const NRMat &rhs)}{Store \code{rhs} as a block with the upper left corner at (\code{fromrow},\code{fromcol}). Plain data only.} \lafunc{void storesubmatrix(const int fromrow, const int fromcol, const NRMat &rhs)}{Store \code{rhs} as a block with the upper left corner at (\code{fromrow},\code{fromcol}). Plain data only.}
\lafunc{void storesubmatrix(const NRVec<int> &rows, const NRVec<int> &cols, const NRMat &rhs)}{Scatter: $A_{r_i c_j}\leftarrow R_{ij}$.} \lafunc{void storesubmatrix(const NRVec<int> &rows, const NRVec<int> &cols, const NRMat &rhs)}{Scatter: $A_{r_i c_j}\leftarrow R_{ij}$.}
\lafunc{const T* diagonalof(NRVec<T> &r, const bool divide = 0, bool cache = false) const}{\code{double} only. For a square matrix copy the diagonal into \code{r}, or with \code{divide} divide \code{r} element-wise by the (nonzero) diagonal elements. For a non-square matrix the diagonal of $A\Tr A$ (squared column norms) is used instead; \code{r} must have \code{ncols()} elements. Returns pointer to \code{r}'s data, or NULL if \code{divide}. \code{cache} is unused. Same interface as in other matrix classes, so that it can be used by iterative solvers as a preconditioner (chapter~\ref{ch:iterative}).} \lafunc{const T* diagonalof(NRVec<T> &r, const bool divide = 0, bool cache = false) const}{\code{double} and \code{float} only. For a square matrix copy the diagonal into \code{r}, or with \code{divide} divide \code{r} element-wise by the (nonzero) diagonal elements. For a non-square matrix the diagonal of $A\Tr A$ (squared column norms) is used instead; \code{r} must have \code{ncols()} elements. Returns pointer to \code{r}'s data, or NULL if \code{divide}. \code{cache} is unused. Same interface as in other matrix classes, so that it can be used by iterative solvers as a preconditioner (chapter~\ref{ch:iterative}).}
\lafunc{void diagonalset(const NRVec<T> &r)}{Set the diagonal; \code{r} has $\min(n,m)$ elements.} \lafunc{void diagonalset(const NRVec<T> &r)}{Set the diagonal; \code{r} has $\min(n,m)$ elements.}
\end{lareference} \end{lareference}
\subsection{Transposition and conjugation} \subsection{Transposition and conjugation}
\begin{lareference} \begin{lareference}
\lafunc{const NRMat transpose(bool conj = false) const}{$A\Tr$, or $A\Hc$ if \code{conj} (complex). \code{double} and \code{complex<double>} only.} \lafunc{const NRMat transpose(bool conj = false) const}{$A\Tr$, or $A\Hc$ if \code{conj} (complex). Floating point types only.}
\lafunc{NRMat& transposeme(const int n = 0)}{In-place transposition of a square matrix (no conjugation); with \code{n>0} only the leading $n\times n$ block is transposed (this also works for a non-square matrix with $n\le\min(\text{rows},\text{cols})$). Dimensions are not changed, so a non-square matrix cannot be transposed in place.} \lafunc{NRMat& transposeme(const int n = 0)}{In-place transposition of a square matrix (no conjugation); with \code{n>0} only the leading $n\times n$ block is transposed (this also works for a non-square matrix with $n\le\min(\text{rows},\text{cols})$). Dimensions are not changed, so a non-square matrix cannot be transposed in place.}
\lafunc{NRMat& conjugateme(); const NRMat conjugate() const}{Complex conjugation in place resp.\ by value; no-op for real types.} \lafunc{NRMat& conjugateme(); const NRMat conjugate() const}{Complex conjugation in place resp.\ by value; no-op for real types.}
\end{lareference} \end{lareference}
@@ -198,7 +208,7 @@ Note that scalar addition and subtraction act on the \emph{diagonal} only, i.e.\
\lafunc{const T sum() const; const NRVec<T> rsum() const; const NRVec<T> csum() const}{Sum of all elements; vector of row sums (length \code{nrows()}, sum over the column index); vector of column sums (length \code{ncols()}).} \lafunc{const T sum() const; const NRVec<T> rsum() const; const NRVec<T> csum() const}{Sum of all elements; vector of row sums (length \code{nrows()}, sum over the column index); vector of column sums (length \code{ncols()}).}
\lafunc{inline const T amax() const; inline const T amin() const}{The element (not its index) with the largest resp.\ smallest absolute value; for complex the measure is $|\mathrm{Re}|+|\mathrm{Im}|$ (BLAS \code{i?amax} convention).} \lafunc{inline const T amax() const; inline const T amin() const}{The element (not its index) with the largest resp.\ smallest absolute value; for complex the measure is $|\mathrm{Re}|+|\mathrm{Im}|$ (BLAS \code{i?amax} convention).}
\lafunc{const typename LA_traits<T>::normtype nonsymmetry() const}{$\sum_{i>j}|A_{ij}-A_{ji}|^2$ (square of the norm, no square root).} \lafunc{const typename LA_traits<T>::normtype nonsymmetry() const}{$\sum_{i>j}|A_{ij}-A_{ji}|^2$ (square of the norm, no square root).}
\lafunc{const typename LA_traits<T>::normtype nonhermiticity() const}{\code{complex<double>} only: $\sum_{i\ge j}|A_{ij}-A_{ji}^*|^2$.} \lafunc{const typename LA_traits<T>::normtype nonhermiticity() const}{Complex types only: $\sum_{i\ge j}|A_{ij}-A_{ji}^*|^2$.}
\lafunc{NRMat<typename LA_traits<T>::normtype> abs() const}{Matrix of element-wise absolute values.} \lafunc{NRMat<typename LA_traits<T>::normtype> abs() const}{Matrix of element-wise absolute values.}
\lafunc{NRMat<typename LA_traits<T>::normtype> diffabs(const NRMat &rhs) const}{Element-wise $|A_{ij}|-|B_{ij}|$.} \lafunc{NRMat<typename LA_traits<T>::normtype> diffabs(const NRMat &rhs) const}{Element-wise $|A_{ij}|-|B_{ij}|$.}
\lafunc{const bool operator==(const NRMat &rhs) const; const bool operator!=(const NRMat &rhs) const}{Equality of dimensions and contents; for plain types a bitwise (\code{memcmp}) comparison, element-wise for nested types.} \lafunc{const bool operator==(const NRMat &rhs) const; const bool operator!=(const NRMat &rhs) const}{Equality of dimensions and contents; for plain types a bitwise (\code{memcmp}) comparison, element-wise for nested types.}
@@ -207,7 +217,7 @@ Note that scalar addition and subtraction act on the \emph{diagonal} only, i.e.\
\subsection{Orthonormalization, inverse, random fill} \subsection{Orthonormalization, inverse, random fill}
\begin{lareference} \begin{lareference}
\lafunc{void orthonormalize(const bool rowcol, const NRSMat<T> *metric = NULL)}{\code{double} only. Modified Gram--Schmidt orthonormalization of the rows (\code{rowcol==true}) or columns (\code{false}) of the matrix in place, optionally with respect to the inner product $x\Tr S y$ given by \code{metric}. A zero vector (or non-positive metric) raises \code{laerror}.} \lafunc{void orthonormalize(const bool rowcol, const NRSMat<T> *metric = NULL)}{\code{double} and \code{float} only. Modified Gram--Schmidt orthonormalization of the rows (\code{rowcol==true}) or columns (\code{false}) of the matrix in place, optionally with respect to the inner product $x\Tr S y$ given by \code{metric}. A zero vector (or non-positive metric) raises \code{laerror}.}
\lafunc{NRMat inverse() const}{$A^{-1}$ via \code{calcinverse} (LAPACK linear solve, chapter~\ref{ch:nonclass}).} \lafunc{NRMat inverse() const}{$A^{-1}$ via \code{calcinverse} (LAPACK linear solve, chapter~\ref{ch:nonclass}).}
\lafunc{NRMat svdinverse(const typename LA_traits<T>::normtype thr = 0) const}{Pseudo-inverse via SVD, singular values below \code{thr} are dropped.} \lafunc{NRMat svdinverse(const typename LA_traits<T>::normtype thr = 0) const}{Pseudo-inverse via SVD, singular values below \code{thr} are dropped.}
\lafunc{NRMat absadjoint(const typename LA_traits<T>::normtype thr = 0) const}{Adjugate (transposed cofactor) matrix computed via SVD up to the factor $\det U\det V$ (a sign, or a phase in the complex case), cf.\ \code{calcsvdinverse} in chapter~\ref{ch:nonclass}. \code{thr} is not used in this mode.} \lafunc{NRMat absadjoint(const typename LA_traits<T>::normtype thr = 0) const}{Adjugate (transposed cofactor) matrix computed via SVD up to the factor $\det U\det V$ (a sign, or a phase in the complex case), cf.\ \code{calcsvdinverse} in chapter~\ref{ch:nonclass}. \code{thr} is not used in this mode.}
@@ -229,6 +239,7 @@ See section~\ref{sec:concepts:io} for the general conventions.
Besides the constructors, \code{operator*(const SparseSMat<T>&)} and \code{gemm} with a \code{SparseMat} Besides the constructors, \code{operator*(const SparseSMat<T>&)} and \code{gemm} with a \code{SparseMat}
factor listed above, \code{NRMat} declares factor listed above, \code{NRMat} declares
\begin{lareference} \begin{lareference}
\lafunc{NRMat & operator+=(const CSRMat<T> &rhs); NRMat & operator-=(const CSRMat<T> &rhs)}{Add/subtract a compressed row sparse matrix (section~\ref{sec:sparse:csr}).}
\lafunc{NRMat & operator+=(const SparseMat<T> &rhs); NRMat & operator-=(const SparseMat<T> &rhs)}{Add/subtract the sparse matrix (defined in \code{sparsemat.h}; a symmetric \code{SparseMat} is unfolded, without conjugation).} \lafunc{NRMat & operator+=(const SparseMat<T> &rhs); NRMat & operator-=(const SparseMat<T> &rhs)}{Add/subtract the sparse matrix (defined in \code{sparsemat.h}; a symmetric \code{SparseMat} is unfolded, without conjugation).}
\end{lareference} \end{lareference}
@@ -322,6 +333,6 @@ NRMat<double> ds = re.oplus(im); // direct sum, 4x4 block diagonal
\lawarning{Writing through \code{operator[]}, \code{operator()} or \code{operator T*()} into a matrix obtained by copy construction or \code{=} modifies all sharing matrices unless \code{copyonwrite()} is called first. In DEBUG builds even \emph{reading} through the non-const operators of a shared matrix raises an error; use a const reference.} \lawarning{Writing through \code{operator[]}, \code{operator()} or \code{operator T*()} into a matrix obtained by copy construction or \code{=} modifies all sharing matrices unless \code{copyonwrite()} is called first. In DEBUG builds even \emph{reading} through the non-const operators of a shared matrix raises an error; use a const reference.}
\lawarning{\code{NRMat(const NRVec<T>&, n, m, offset)} shares the vector data (without MATPTR). With a nonzero offset, if the matrix happens to be the last owner of the data, it will \code{delete[]} a pointer that is not the beginning of the allocated block; the constructor therefore prints a warning. This is by design (a view without copying, avoiding the cost of a copy); the condition of use is: keep the vector alive longer than the \lawarning{\code{NRMat(const NRVec<T>&, n, m, offset)} shares the vector data (without MATPTR). With a nonzero offset, if the matrix happens to be the last owner of the data, it will \code{delete[]} a pointer that is not the beginning of the allocated block; the constructor therefore prints a warning. This is by design (a view without copying, avoiding the cost of a copy); the condition of use is: keep the vector alive longer than the
matrix, or make an independent copy (\code{|=}) if the matrix has to outlive it.} matrix, or make an independent copy (\code{|=}) if the matrix has to outlive it.}
\lawarning{Many numerical members (\code{gemm}, \code{transpose}, \code{norm}, \ldots) exist only for \code{double} and \code{complex<double>} (Table~\ref{tab:mat:types}); with other element types the program fails to link.} \lawarning{Many numerical members (\code{gemm}, \code{transpose}, \code{norm}, \ldots) exist only for the floating point types \code{double}, \code{float}, \code{complex<double>} and \code{complex<float>} (Table~\ref{tab:mat:types}); with other element types the program fails to link.}
\lawarning{\code{transposeme()} does not conjugate and works only on square matrices (or square leading blocks); use \code{transpose(true)} for $A\Hc$ or for a non-square matrix.} \lawarning{\code{transposeme()} does not conjugate and works only on square matrices (or square leading blocks); use \code{transpose(true)} for $A\Hc$ or for a non-square matrix.}
\lawarning{In the non-DEBUG build no dimension checks are done in most operations (\code{operator+=}, \code{gemm}, \code{operator=(const T&)}, \ldots); mismatches lead to silent memory corruption.} \lawarning{In the non-DEBUG build no dimension checks are done in most operations (\code{operator+=}, \code{gemm}, \code{operator=(const T&)}, \ldots); mismatches lead to silent memory corruption.}
+33 -32
View File
@@ -6,8 +6,8 @@ This chapter describes the free (non-member) functions operating on the dense cl
\code{NRVec}, \code{NRMat} and \code{NRSMat}: linear systems, determinants, inverses, Cholesky \code{NRVec}, \code{NRMat} and \code{NRSMat}: linear systems, determinants, inverses, Cholesky
decomposition, symmetric/hermitian and general eigenvalue problems, singular value decomposition, decomposition, symmetric/hermitian and general eigenvalue problems, singular value decomposition,
matrix functions, and a few auxiliary products and conversions. Most of them are thin wrappers of matrix functions, and a few auxiliary products and conversions. Most of them are thin wrappers of
LAPACK routines (\header{nonclass.cc}); they exist for \code{T}${}={}$\code{double} and LAPACK routines (\header{nonclass.cc}); they exist for \code{T}${}={}$\code{double}, \code{float},
\code{std::complex<double>} only. The templates in \header{matexp.h} (matrix polynomials, matrix \code{std::complex<double>} and \code{std::complex<float>} only. The templates in \header{matexp.h} (matrix polynomials, matrix
exponential, $\exp(A)\,x$, BCH expansion) are generic: they work for every class that provides the few exponential, $\exp(A)\,x$, BCH expansion) are generic: they work for every class that provides the few
operations listed in section~\ref{sec:nonclass:matexp}, including sparse matrices operations listed in section~\ref{sec:nonclass:matexp}, including sparse matrices
(chapter~\ref{ch:sparse}) and user-defined matrix classes which only implement \code{gemv}. (chapter~\ref{ch:sparse}) and user-defined matrix classes which only implement \code{gemv}.
@@ -44,8 +44,8 @@ growing leading block.
(sec.~\ref{sec:concepts:errors}), e.g.\ ``singular matrix'' or ``convergence problem''. Exceptions: (sec.~\ref{sec:concepts:errors}), e.g.\ ``singular matrix'' or ``convergence problem''. Exceptions:
\code{linear_solve_x} and \code{multiply_by_inverse} return \code{info}. \code{linear_solve_x} and \code{multiply_by_inverse} return \code{info}.
\paragraph{Element types and availability.} The functions are declared for \code{double} and \paragraph{Element types and availability.} The functions are declared for \code{double}, \code{float},
\code{std::complex<double>} by the macro \code{declare_la}, but not all declared complex variants are \code{std::complex<double>} and \code{std::complex<float>} by the macro \code{declare_la}, but not all declared complex variants are
defined in \header{nonclass.cc}. Calling an undefined one gives a link error defined in \header{nonclass.cc}. Calling an undefined one gives a link error
(table~\ref{tab:nonclass:avail}). (table~\ref{tab:nonclass:avail}).
\begin{table}[htbp] \begin{table}[htbp]
@@ -54,15 +54,15 @@ defined in \header{nonclass.cc}. Calling an undefined one gives a link error
\toprule \toprule
Function & Available for \\ Function & Available for \\
\midrule \midrule
\texttt{linear\_solve(NRMat\&, NRMat*, ...)} & \texttt{double}, \texttt{complex<double>} \\ \texttt{linear\_solve(NRMat\&, NRMat*, ...)} & \texttt{double}, \texttt{float}, \texttt{complex<double>}, \texttt{complex<float>} \\
\texttt{linear\_solve(NRMat\&, NRVec\&, ...)}, \texttt{linear\_solve(NRSMat\&, ...)} & \texttt{double}, \texttt{complex<double>} (complex \texttt{NRSMat}: hermitian) \\ \texttt{linear\_solve(NRMat\&, NRVec\&, ...)}, \texttt{linear\_solve(NRSMat\&, ...)} & \texttt{double}, \texttt{float}, \texttt{complex<double>}, \texttt{complex<float>} (complex \texttt{NRSMat}: hermitian) \\
\texttt{determinant}, \texttt{determinant\_destroy} & \texttt{NRMat}, \texttt{NRSMat}, real and complex \\ \texttt{determinant}, \texttt{determinant\_destroy} & \texttt{NRMat}, \texttt{NRSMat}, real and complex \\
\texttt{diagonalize}, \texttt{singular\_decomposition}, \texttt{cholesky}, \texttt{diagofproduct}, \texttt{diagonalize}, \texttt{singular\_decomposition}, \texttt{cholesky}, \texttt{diagofproduct},
\texttt{trace2(NRMat,NRMat)}, \texttt{linear\_solve\_x}, \texttt{multiply\_by\_inverse}, \texttt{MatrixNorm}, \texttt{CondNumber} & \texttt{double}, \texttt{complex<double>} \\ \texttt{trace2(NRMat,NRMat)}, \texttt{linear\_solve\_x}, \texttt{multiply\_by\_inverse}, \texttt{MatrixNorm}, \texttt{CondNumber} & \texttt{double}, \texttt{float}, \texttt{complex<double>}, \texttt{complex<float>} \\
\texttt{gdiagonalize} & real matrix (real or complex eigenvector storage), complex matrix \\ \texttt{gdiagonalize} & real matrix (real or complex eigenvector storage), complex matrix \\
\texttt{trace2} with \texttt{NRSMat} & \texttt{double}, \texttt{complex<double>} (hermitian) \\ \texttt{trace2} with \texttt{NRSMat} & \texttt{double}, \texttt{float}, \texttt{complex<double>}, \texttt{complex<float>} (hermitian) \\
\texttt{diagonalize3}, \texttt{realmatrixfunction}, \texttt{complexmatrixfunction}, \texttt{diagonalize3}, \texttt{realmatrixfunction}, \texttt{complexmatrixfunction},
\texttt{adjustphases}, \texttt{xcopy}, \texttt{xaxpy}, \texttt{xdot} & \texttt{double} only \\ \texttt{adjustphases}, \texttt{xcopy}, \texttt{xaxpy}, \texttt{xdot} & \texttt{double}, \texttt{float} \\
templates of \texttt{matexp.h}, \texttt{power}, \texttt{commutator}, \texttt{twoside\_transform}, \dots & any type providing the needed operations \\ templates of \texttt{matexp.h}, \texttt{power}, \texttt{commutator}, \texttt{twoside\_transform}, \dots & any type providing the needed operations \\
\bottomrule \bottomrule
\end{tabular} \end{tabular}
@@ -106,27 +106,27 @@ $n_{\mathrm{rhs}}=0$).}
\lafunc{void linear_solve(NRSMat<T> &a, NRMat<T> *b, T *det=0, int n=0); void linear_solve(NRSMat<T> &a, NRVec<T> &b, T *det=0, int n=0)}{Symmetric (real) or hermitian (complex), possibly indefinite $A$ in packed storage, \code{dspsv}/\code{zhpsv} \lafunc{void linear_solve(NRSMat<T> &a, NRMat<T> *b, T *det=0, int n=0); void linear_solve(NRSMat<T> &a, NRVec<T> &b, T *det=0, int n=0)}{Symmetric (real) or hermitian (complex), possibly indefinite $A$ in packed storage, \code{dspsv}/\code{zhpsv}
(Bunch--Kaufman factorization; the determinant is computed from its $1\times1$ and $2\times2$ blocks and is real in the hermitian case). (Bunch--Kaufman factorization; the determinant is computed from its $1\times1$ and $2\times2$ blocks and is real in the hermitian case).
\code{n} selects a leading block, which is contiguous in the packed storage.} \code{n} selects a leading block, which is contiguous in the packed storage.}
\lafunc{int linear_solve_x(NRMat<T> &A, T *B, const int rhsCount, const int eqCount, const bool eq, const bool saveA, double *rcond)}{Expert solver \lafunc{int linear_solve_x(NRMat<T> &A, T *B, const int rhsCount, const int eqCount, const bool eq, const bool saveA, typename LA_traits<T>::normtype *rcond)}{Expert solver
(\code{dgesvx}/\code{zgesvx}): solves $A x=b$ for \code{rhsCount} right-hand sides stored consecutively (each of length \code{eqCount}) in the raw (\code{dgesvx}/\code{zgesvx}): solves $A x=b$ for \code{rhsCount} right-hand sides stored consecutively (each of length \code{eqCount}) in the raw
array \code{B}, using the leading \code{eqCount}${}\times{}$\code{eqCount} block of \code{A}. \code{eq}: equilibrate $A$ (for badly conditioned matrices); array \code{B}, using the leading \code{eqCount}${}\times{}$\code{eqCount} block of \code{A}. \code{eq}: equilibrate $A$ (for badly conditioned matrices);
then \code{A} is overwritten unless \code{saveA}. The reciprocal condition number is stored in \code{*rcond} if non-NULL. Returns LAPACK \code{info} then \code{A} is overwritten unless \code{saveA}. The reciprocal condition number is stored in \code{*rcond} if non-NULL. Returns LAPACK \code{info}
(0 success, $>0$ singular or ill-conditioned). The solution replaces \code{B}.} (0 success, $>0$ singular or ill-conditioned). The solution replaces \code{B}.}
\lafunc{int multiply_by_inverse(NRMat<T> &A, NRMat<T> &B, bool useEq=false, double *rcond=NULL)}{$B\leftarrow A B^{-1}$ for square $A$, $B$ via \lafunc{int multiply_by_inverse(NRMat<T> &A, NRMat<T> &B, bool useEq=false, typename LA_traits<T>::normtype *rcond=NULL)}{$B\leftarrow A B^{-1}$ for square $A$, $B$ via
\code{dgesvx}/\code{zgesvx}; \code{useEq} as \code{eq} above. \code{A} is not modified (with \code{useEq} a temporary copy is \code{dgesvx}/\code{zgesvx}; \code{useEq} as \code{eq} above. \code{A} is not modified (with \code{useEq} a temporary copy is
scaled). Returns \code{info}.} scaled). Returns \code{info}.}
\lafunc{const NRMat<T> calcinverse(NRMat<T> a, T *det=NULL)}{$A^{-1}$ by \code{linear_solve} with unit right-hand sides; optionally the determinant. \lafunc{const NRMat<T> calcinverse(NRMat<T> a, T *det=NULL)}{$A^{-1}$ by \code{linear_solve} with unit right-hand sides; optionally the determinant.
\code{NRMat::inverse()} (chapter~\ref{ch:mat}) calls it.} \code{NRMat::inverse()} (chapter~\ref{ch:mat}) calls it.}
\lafunc{const NRMat<T> calcsvdinverse(NRMat<T> a, double thr=0, bool do_absadjoint=false)}{Inverse via SVD, $V\,\mathrm{diag}(1/s_i)\,U\Hc$, \lafunc{const NRMat<T> calcsvdinverse(NRMat<T> a, typename LA_traits<T>::normtype thr=0, bool do_absadjoint=false)}{Inverse via SVD, $V\,\mathrm{diag}(1/s_i)\,U\Hc$,
singular values $s_i<$\code{thr} are treated as zero (pseudoinverse). With \code{do_absadjoint} returns singular values $s_i<$\code{thr} are treated as zero (pseudoinverse). With \code{do_absadjoint} returns
$V\,\mathrm{diag}(\prod_{j\ne i}s_j)\,U\Hc$, the adjugate matrix up to the phase $\det U\det V$. Square matrices only.} $V\,\mathrm{diag}(\prod_{j\ne i}s_j)\,U\Hc$, the adjugate matrix up to the phase $\det U\det V$. Square matrices only.}
\lafunc{NRVec<T> svd_solve(NRMat<T> &a, const NRVec<T> &b, double thres=1e-12, int *nullity=NULL); NRMat<T> svd_solve(NRMat<T> &a, const NRMat<T> &b, double thres=1e-12, int *nullity=NULL)}{Least-squares \lafunc{NRVec<T> svd_solve(NRMat<T> &a, const NRVec<T> &b, normtype thres=LA_precision<normtype>::svd_threshold(), int *nullity=NULL); NRMat<T> svd_solve(NRMat<T> &a, const NRMat<T> &b, normtype thres=LA_precision<normtype>::svd_threshold(), int *nullity=NULL)}{Least-squares
solution $x=V\,\mathrm{diag}(1/s_i)\,U\Hc b$ discarding singular values $\le$\code{thres} (no contamination by the null space); solution $x=V\,\mathrm{diag}(1/s_i)\,U\Hc b$ discarding singular values $\le$\code{thres} (no contamination by the null space);
the number of discarded ones is stored in \code{*nullity}. Here (unlike \code{linear_solve}) the right-hand sides are the the number of discarded ones is stored in \code{*nullity}. Here (unlike \code{linear_solve}) the right-hand sides are the
\emph{columns} of the matrix \code{b} ($m\times n_{\mathrm{rhs}}$); \code{a} ($m\times n$, $m\ge n$) is destroyed.} \emph{columns} of the matrix \code{b} ($m\times n_{\mathrm{rhs}}$); \code{a} ($m\times n$, $m\ge n$) is destroyed.}
\lafunc{const LA_traits<MAT>::elementtype determinant(MAT a)}{Determinant by LU (\code{NRMat}) or Bunch--Kaufman (\code{NRSMat}) factorization of a copy.} \lafunc{const LA_traits<MAT>::elementtype determinant(MAT a)}{Determinant by LU (\code{NRMat}) or Bunch--Kaufman (\code{NRSMat}) factorization of a copy.}
\lafunc{const LA_traits<MAT>::elementtype determinant_destroy(MAT &a)}{The same, overwriting \code{a}. Unless \code{HAS_MKL} is defined, a dummy right-hand side \lafunc{const LA_traits<MAT>::elementtype determinant_destroy(MAT &a)}{The same, overwriting \code{a}. Unless \code{HAS_MKL} is defined, a dummy right-hand side
is passed to \code{linear_solve} (work-around for OpenBLAS, which skips the factorization without right-hand sides).} is passed to \code{linear_solve} (work-around for OpenBLAS, which skips the factorization without right-hand sides).}
\lafunc{void cholesky(NRMat<double> &a, bool upper=1); void cholesky(NRMat<std::complex<double> > &a, bool upper=1)}{Cholesky factorization of a \lafunc{void cholesky(NRMat<T> &a, bool upper=1)}{Cholesky factorization of a
positive definite matrix in place (\code{dpotrf}/\code{zpotrf}). \code{upper=1}: \code{a} becomes the upper triangular $U$ with positive definite matrix in place (\code{dpotrf}/\code{zpotrf}). \code{upper=1}: \code{a} becomes the upper triangular $U$ with
$A=U\Hc U$; \code{upper=0}: lower triangular $L$ with $A=L L\Hc$. The other triangle is zeroed. Raises \code{laerror} if $A$ is not positive definite.} $A=U\Hc U$; \code{upper=0}: lower triangular $L$ with $A=L L\Hc$. The other triangle is zeroed. Raises \code{laerror} if $A$ is not positive definite.}
\end{lareference} \end{lareference}
@@ -138,15 +138,15 @@ Eigenvalues are always returned in \textbf{ascending order} (LAPACK). Only the \
(row index ${}\ge{}$ column index) of an \code{NRMat} is referenced (verified); \code{NRSMat} stores only that (row index ${}\ge{}$ column index) of an \code{NRMat} is referenced (verified); \code{NRSMat} stores only that
triangle and is interpreted as hermitian (sec.~\ref{sec:smat:hermitian}). triangle and is interpreted as hermitian (sec.~\ref{sec:smat:hermitian}).
\begin{lareference} \begin{lareference}
\lafunc{void diagonalize(NRMat<T> &a, NRVec<double> &w, const bool eivec=1, const bool corder=1, int n=0, NRMat<T> *b=NULL, const int itype=1)}{Eigenvalues \lafunc{void diagonalize(NRMat<T> &a, NRVec<typename LA_traits<T>::normtype> &w, const bool eivec=1, const bool corder=1, int n=0, NRMat<T> *b=NULL, const int itype=1)}{Eigenvalues
\code{w} (size must equal \code{a.nrows()}) and, if \code{eivec}, eigenvectors stored in \code{a} (columns for \code{corder=1}, see table~\ref{tab:nonclass:eivec}) \code{w} (size must equal \code{a.nrows()}) and, if \code{eivec}, eigenvectors stored in \code{a} (columns for \code{corder=1}, see table~\ref{tab:nonclass:eivec})
of a real symmetric or complex hermitian matrix (\code{dsyev}/\code{zheev}). If \code{b} is given, the generalized problem with positive definite $B$ of a real symmetric or complex hermitian matrix (\code{dsyev}/\code{zheev}). If \code{b} is given, the generalized problem with positive definite $B$
is solved (\code{dsygv}/\code{zhegv}): \code{itype=1}: $A x=\lambda B x$, \code{itype=2}: $A B x=\lambda x$, \code{itype=3}: $B A x=\lambda x$; is solved (\code{dsygv}/\code{zhegv}): \code{itype=1}: $A x=\lambda B x$, \code{itype=2}: $A B x=\lambda x$, \code{itype=3}: $B A x=\lambda x$;
eigenvectors are $B$-normalized ($x\Hc B x=1$ for \code{itype} 1, 2), \code{b} is overwritten by its Cholesky factor.} eigenvectors are $B$-normalized ($x\Hc B x=1$ for \code{itype} 1, 2), \code{b} is overwritten by its Cholesky factor.}
\lafunc{void diagonalize(NRSMat<T> &a, NRVec<double> &w, NRMat<T> *v, const bool corder=1, int n=0, NRSMat<T> *b=NULL, const int itype=1)}{The same for packed \lafunc{void diagonalize(NRSMat<T> &a, NRVec<typename LA_traits<T>::normtype> &w, NRMat<T> *v, const bool corder=1, int n=0, NRSMat<T> *b=NULL, const int itype=1)}{The same for packed
storage (\code{dspev}/\code{zhpev}, \code{dspgv}/\code{zhpgv}); eigenvectors go to \code{*v} (square, at least $n\times n$) or are not computed if \code{v==NULL}. storage (\code{dspev}/\code{zhpev}, \code{dspgv}/\code{zhpgv}); eigenvectors go to \code{*v} (square, at least $n\times n$) or are not computed if \code{v==NULL}.
\code{a} (and \code{b}) are destroyed.} \code{a} (and \code{b}) are destroyed.}
\lafunc{void diagonalize3(NRVec<double> &d, NRVec<double> &d1, NRMat<double> *v, const bool corder=1, int n0=0)}{Real symmetric tridiagonal matrix \lafunc{void diagonalize3(NRVec<double> &d, NRVec<double> &d1, NRMat<double> *v, const bool corder=1, int n0=0)}{(also for \code{float}) Real symmetric tridiagonal matrix
(\code{dsteqr}) with diagonal \code{d} and sub-diagonal \code{d1} (size ${}\ge n-1$). On return \code{d} holds the eigenvalues (ascending), \code{d1} is destroyed, (\code{dsteqr}) with diagonal \code{d} and sub-diagonal \code{d1} (size ${}\ge n-1$). On return \code{d} holds the eigenvalues (ascending), \code{d1} is destroyed,
\code{*v} (exactly $n\times n$) the eigenvectors. \code{n0>0} uses only the first \code{n0} elements. Used by \code{lanczos} (chapter~\ref{ch:iterative}).} \code{*v} (exactly $n\times n$) the eigenvectors. \code{n0>0} uses only the first \code{n0} elements. Used by \code{lanczos} (chapter~\ref{ch:iterative}).}
\end{lareference} \end{lareference}
@@ -158,7 +158,7 @@ may contain zeros for infinite eigenvalues). Right (\code{vr}) and left (\code{v
pointers are non-NULL; storage according to table~\ref{tab:nonclass:eivec}. Without sorting, the eigenvalues come in pointers are non-NULL; storage according to table~\ref{tab:nonclass:eivec}. Without sorting, the eigenvalues come in
LAPACK order (unsorted, complex conjugate pairs adjacent with positive imaginary part first). LAPACK order (unsorted, complex conjugate pairs adjacent with positive imaginary part first).
\begin{lareference} \begin{lareference}
\lafunc{void gdiagonalize(NRMat<double> &a, NRVec<double> &wr, NRVec<double> &wi, NRMat<double> *vl=NULL, NRMat<double> *vr=NULL, const bool corder=1, int n=0, const int sorttype=0, const int biorthonormalize=0, NRMat<double> *b=NULL, NRVec<double> *beta=NULL)}{Real \lafunc{void gdiagonalize(NRMat<double> &a, NRVec<double> &wr, NRVec<double> &wi, NRMat<double> *vl=NULL, NRMat<double> *vr=NULL, const bool corder=1, int n=0, const int sorttype=0, const int biorthonormalize=0, NRMat<double> *b=NULL, NRVec<double> *beta=NULL)}{(also for \code{float}) Real
matrix, eigenvalues $w_r+\mathrm{i}\,w_i$, eigenvectors in the real storage of \code{dgeev}: for a real eigenvalue the vector is real; for a complex pair matrix, eigenvalues $w_r+\mathrm{i}\,w_i$, eigenvectors in the real storage of \code{dgeev}: for a real eigenvalue the vector is real; for a complex pair
$(j,j{+}1)$ the vectors $j$ and $j{+}1$ hold the real and imaginary part. Verified: the right eigenvector belonging to $(j,j{+}1)$ the vectors $j$ and $j{+}1$ hold the real and imaginary part. Verified: the right eigenvector belonging to
$w_{r,j}+\mathrm{i}w_{i,j}$ ($w_{i,j}>0$) is $\mathrm{Re}-\mathrm{i}\,\mathrm{Im}$, and $\mathrm{Re}+\mathrm{i}\,\mathrm{Im}$ of \code{vl} satisfies $y\Tr A=\lambda y\Tr$ -- $w_{r,j}+\mathrm{i}w_{i,j}$ ($w_{i,j}>0$) is $\mathrm{Re}-\mathrm{i}\,\mathrm{Im}$, and $\mathrm{Re}+\mathrm{i}\,\mathrm{Im}$ of \code{vl} satisfies $y\Tr A=\lambda y\Tr$ --
@@ -167,15 +167,15 @@ note that this sign is opposite to the \code{dgeev} documentation.
2 real eigenvalues first, then complex ones, each group ordered as for 1. Eigenvectors are permuted accordingly. 2 real eigenvalues first, then complex ones, each group ordered as for 1. Eigenvectors are permuted accordingly.
\code{biorthonormalize} (needs \code{vl} and \code{vr}, not with \code{b}): 1 scales the left, 2 the right eigenvectors so that $y_k\Tr x_k=1$; \code{biorthonormalize} (needs \code{vl} and \code{vr}, not with \code{b}): 1 scales the left, 2 the right eigenvectors so that $y_k\Tr x_k=1$;
complex pairs are rotated to biorthonormality. \code{a} is destroyed (and used as scratch space).} complex pairs are rotated to biorthonormality. \code{a} is destroyed (and used as scratch space).}
\lafunc{void gdiagonalize(NRMat<T> &a, NRVec<std::complex<double> > &w, NRMat<std::complex<double> > *vl=NULL, NRMat<std::complex<double> > *vr=NULL, const bool corder=1, int n=0, const int sorttype=0, const int biorthonormalize=0, NRMat<T> *b=NULL, NRVec<T> *beta=NULL)}{Template \lafunc{void gdiagonalize(NRMat<T> &a, NRVec<typename LA_traits<T>::complextype> &w, NRMat<typename LA_traits<T>::complextype> *vl=NULL, NRMat<typename LA_traits<T>::complextype> *vr=NULL, const bool corder=1, int n=0, const int sorttype=0, const int biorthonormalize=0, NRMat<T> *b=NULL, NRVec<T> *beta=NULL)}{Template
specialized for \code{T=double} (calls the previous routine and converts to complex eigenvalues and eigenvectors) and \code{T=complex<double>} (\code{zgeev}/\code{zggev}). specialized for \code{T=double}, \code{float} (calls the previous routine and converts to complex eigenvalues and eigenvectors) and \code{T=complex<double>}, \code{complex<float>} (\code{zgeev}/\code{zggev}, \code{cgeev}/\code{cggev}).
For the complex matrix, \code{sorttype>0} raises \code{laerror} (not implemented) and \code{biorthonormalize} scales so that $\bar y_k\Tr x_k=1$ in the row storage.} For the complex matrix, \code{sorttype>0} raises \code{laerror} (not implemented) and \code{biorthonormalize} scales so that $\bar y_k\Tr x_k=1$ in the row storage.}
\lafunc{void gdiagonalize(NRMat<std::complex<double> > &a, NRVec<double> &wr, NRVec<double> &wi, NRMat<std::complex<double> > *vl=NULL, NRMat<std::complex<double> > *vr=NULL, const bool corder=1, int n=0, const int sorttype=0, const int biorthonormalize=0, NRMat<std::complex<double> > *b=NULL, NRVec<std::complex<double> > *beta=NULL)}{Complex \lafunc{void gdiagonalize(NRMat<std::complex<double> > &a, NRVec<double> &wr, NRVec<double> &wi, NRMat<std::complex<double> > *vl=NULL, NRMat<std::complex<double> > *vr=NULL, const bool corder=1, int n=0, const int sorttype=0, const int biorthonormalize=0, NRMat<std::complex<double> > *b=NULL, NRVec<std::complex<double> > *beta=NULL)}{(also for \code{complex<float>}) Complex
matrix with real and imaginary parts of the eigenvalues in separate vectors; exists to make \code{davidson} compile for complex \code{T}.} matrix with real and imaginary parts of the eigenvalues in separate vectors; exists to make \code{davidson} compile for complex \code{T}.}
\end{lareference} \end{lareference}
\section{Singular value decomposition}\label{sec:nonclass:svd} \section{Singular value decomposition}\label{sec:nonclass:svd}
\begin{lareference} \begin{lareference}
\lafunc{void singular_decomposition(NRMat<T> &a, NRMat<T> *u, NRVec<double> &s, NRMat<T> *v, const bool vnotdagger=0, int m=0, int n=0)}{SVD \lafunc{void singular_decomposition(NRMat<T> &a, NRMat<T> *u, NRVec<typename LA_traits<T>::normtype> &s, NRMat<T> *v, const bool vnotdagger=0, int m=0, int n=0)}{SVD
$A=U\,\mathrm{diag}(s)\,V\Hc$ of the $m\times n$ matrix \code{a} (\code{dgesvd}/\code{zgesvd}; \code{m}, \code{n} select a leading block, default whole matrix). $A=U\,\mathrm{diag}(s)\,V\Hc$ of the $m\times n$ matrix \code{a} (\code{dgesvd}/\code{zgesvd}; \code{m}, \code{n} select a leading block, default whole matrix).
\code{s} (size ${}\ge\min(m,n)$) receives the singular values in descending order. \code{u}: \code{NULL} (not computed), $m\times m$ (all columns) or \code{s} (size ${}\ge\min(m,n)$) receives the singular values in descending order. \code{u}: \code{NULL} (not computed), $m\times m$ (all columns) or
$m\times\min(m,n)$ (economy size, chosen by \code{u} being non-square). \code{v}: \code{NULL}, $n\times n$ or $\min(m,n)\times n$, receives $V\Hc$ $m\times\min(m,n)$ (economy size, chosen by \code{u} being non-square). \code{v}: \code{NULL}, $n\times n$ or $\min(m,n)\times n$, receives $V\Hc$
@@ -186,11 +186,12 @@ $m\times\min(m,n)$ (economy size, chosen by \code{u} being non-square). \code{v}
Functions of matrices computed via the eigendecomposition $f(A)=V f(\Lambda) V^{-1}$. For general matrices Functions of matrices computed via the eigendecomposition $f(A)=V f(\Lambda) V^{-1}$. For general matrices
the eigenvalue problem is solved in complex arithmetic and the result converted back to the element type of the the eigenvalue problem is solved in complex arithmetic and the result converted back to the element type of the
argument; for a real argument the imaginary part is \emph{silently dropped} (its size is not checked). The matrix must be argument; for a real argument the imaginary part is \emph{silently dropped} (its size is not checked). The matrix must be
diagonalizable; nothing is done about defective or ill-conditioned eigenvector matrices. diagonalizable; nothing is done about defective or ill-conditioned eigenvector matrices. The functions listed below with
\code{double} in the signature exist also with \code{float} (and \code{complex<float>} in place of \code{complex<double>}).
\begin{lareference} \begin{lareference}
\lafunc{NRMat<T> matrixfunction(NRSMat<T> a, double (*f)(double))}{$f(A)$ for a real symmetric or complex hermitian packed matrix, real function \code{f} \lafunc{NRMat<T> matrixfunction(NRSMat<T> a, typename LA_traits<T>::normtype (*f)(typename LA_traits<T>::normtype))}{$f(A)$ for a real symmetric or complex hermitian packed matrix, real function \code{f}
of the (real) eigenvalues.} of the (real) eigenvalues.}
\lafunc{NRMat<T> matrixfunction(NRMat<T> a, std::complex<double> (*f)(const std::complex<double> &))}{$f(A)$ for a general real or complex \lafunc{NRMat<T> matrixfunction(NRMat<T> a, typename LA_traits<T>::complextype (*f)(const typename LA_traits<T>::complextype &))}{$f(A)$ for a general real or complex
matrix by \code{gdiagonalize}, $f(A)=\sum_k f(\lambda_k)\,x_k y_k\Tr/(y_k\Tr x_k)$.} matrix by \code{gdiagonalize}, $f(A)=\sum_k f(\lambda_k)\,x_k y_k\Tr/(y_k\Tr x_k)$.}
\lafunc{NRMat<double> realmatrixfunction(NRMat<double> a, double (*f)(double))}{$f(A)$ for a real symmetric matrix in full storage (only the lower triangle is used).} \lafunc{NRMat<double> realmatrixfunction(NRMat<double> a, double (*f)(double))}{$f(A)$ for a real symmetric matrix in full storage (only the lower triangle is used).}
\lafunc{NRMat<std::complex<double> > complexmatrixfunction(NRMat<double> a, double (*fre)(double), double (*fim)(double))}{Complex function \lafunc{NRMat<std::complex<double> > complexmatrixfunction(NRMat<double> a, double (*fre)(double), double (*fim)(double))}{Complex function
@@ -223,8 +224,8 @@ so they can be used with implicitly defined (``direct'') matrices as in chapter~
needs \code{size()}, \code{operator*=}, \code{axpy} and \code{resize()}. needs \code{size()}, \code{operator*=}, \code{axpy} and \code{resize()}.
The exponential uses scaling and squaring: the power $p$ is chosen so that $\|A\|/2^p\lesssim 0.75$ The exponential uses scaling and squaring: the power $p$ is chosen so that $\|A\|/2^p\lesssim 0.75$
(\code{mynextpow2}), the Taylor series of the scaled argument is truncated when the next term would be below $10^{-14}$ (\code{mynextpow2}), the Taylor series of the scaled argument is truncated when the next term would be below
relative to the norm, and the result is squared $p$ times. The scaling is put into the Taylor coefficients, \code{LA_precision<normtype>::taylor_precision()} ($10^{-14}$ for double, $10^{-7}$ for float) relative to the norm, and the result is squared $p$ times. The scaling is put into the Taylor coefficients,
so the argument itself is not copied. \code{maxpower} (if ${}\ge0$) limits $p$, \code{maxtaylor} (if ${}\ge0$) limits so the argument itself is not copied. \code{maxpower} (if ${}\ge0$) limits $p$, \code{maxtaylor} (if ${}\ge0$) limits
the order of the Taylor polynomial, which is useful and exact for nilpotent matrices (e.g.\ coupled-cluster excitation the order of the Taylor polynomial, which is useful and exact for nilpotent matrices (e.g.\ coupled-cluster excitation
operators). operators).
@@ -285,7 +286,7 @@ with two \code{gemm} calls; \code{trx}, \code{tryy} transpose the factors (no co
\section{Norms and condition number}\label{sec:nonclass:norms} \section{Norms and condition number}\label{sec:nonclass:norms}
\begin{lareference} \begin{lareference}
\lafunc{typename LA_traits<MAT>::normtype MatrixNorm(const MAT &A, const char norm)}{Matrix norm by \code{dlange}/\code{zlange}, implemented for \lafunc{typename LA_traits<MAT>::normtype MatrixNorm(const MAT &A, const char norm)}{Matrix norm by \code{dlange}/\code{zlange}, implemented for
\code{NRMat<double>} and \code{NRMat<std::complex<double> >} (explicit specializations of the template, which can be added for other types). \code{NRMat} of \code{double}, \code{float}, \code{std::complex<double>} and \code{std::complex<float>} (explicit specializations of the template, which can be added for other types).
\code{'1'} or \code{'O'}: 1-norm (maximum column sum); \code{'I'}: $\infty$-norm (maximum row sum); \code{'F'} or \code{'E'}: Frobenius norm; \code{'1'} or \code{'O'}: 1-norm (maximum column sum); \code{'I'}: $\infty$-norm (maximum row sum); \code{'F'} or \code{'E'}: Frobenius norm;
\code{'M'}: largest absolute value of an element (lower case accepted). Works for non-square matrices.} \code{'M'}: largest absolute value of an element (lower case accepted). Works for non-square matrices.}
\lafunc{typename LA_traits<MAT>::normtype CondNumber(const MAT &A, const char norm)}{Condition number $\|A\|\,\|A^{-1}\|$ of a square matrix in the 1-norm \lafunc{typename LA_traits<MAT>::normtype CondNumber(const MAT &A, const char norm)}{Condition number $\|A\|\,\|A^{-1}\|$ of a square matrix in the 1-norm
@@ -296,10 +297,10 @@ infinity for a singular matrix. It is LAPACK's estimate, a lower bound that is u
\section{Real and imaginary parts}\label{sec:nonclass:complex} \section{Real and imaginary parts}\label{sec:nonclass:complex}
\begin{lareference} \begin{lareference}
\lafunc{const LA_traits<T>::realtype realpart(const T &a); const LA_traits<T>::realtype imagpart(const T &a)}{Real or imaginary part as a real object; \lafunc{const LA_traits<T>::realtype realpart(const T &a); const LA_traits<T>::realtype imagpart(const T &a)}{Real or imaginary part as a real object;
specialized for \code{NRVec}, \code{NRMat}, \code{NRSMat} and \code{SparseMat} over \code{complex<double>}.} specialized for \code{NRVec}, \code{NRMat}, \code{NRSMat}, \code{SparseMat} and \code{CSRMat} over \code{complex<double>} and \code{complex<float>}.}
\lafunc{const LA_traits<T>::complextype realmatrix(const T &a); const LA_traits<T>::complextype imagmatrix(const T &a)}{Complex object $A$ resp.\ $\mathrm{i}A$ from a real one; \lafunc{const LA_traits<T>::complextype realmatrix(const T &a); const LA_traits<T>::complextype imagmatrix(const T &a)}{Complex object $A$ resp.\ $\mathrm{i}A$ from a real one;
for \code{NRMat<double>} and \code{SparseSMat<double>}.} for \code{NRMat}, \code{SparseSMat} and \code{CSRMat} of \code{double} and \code{float}.}
\lafunc{const LA_traits<T>::complextype complexmatrix(const T &re, const T &im)}{Complex object $\mathrm{re}+\mathrm{i}\,\mathrm{im}$; for \code{NRMat<double>} and \code{SparseSMat<double>}.} \lafunc{const LA_traits<T>::complextype complexmatrix(const T &re, const T &im)}{Complex object $\mathrm{re}+\mathrm{i}\,\mathrm{im}$; for \code{NRMat}, \code{SparseSMat} and \code{CSRMat} of \code{double} and \code{float}.}
\end{lareference} \end{lareference}
For the same purpose the classes provide \code{complexify()} (chapters~\ref{ch:vec}, \ref{ch:mat}). For the same purpose the classes provide \code{complexify()} (chapters~\ref{ch:vec}, \ref{ch:mat}).
The real and imaginary part functions are CPU only (sec.~\ref{sec:concepts:gpu}). The real and imaginary part functions are CPU only (sec.~\ref{sec:concepts:gpu}).
@@ -314,7 +315,7 @@ Application from the source: overlap of Slater determinants with signed spin-orb
\lafunc{NRMat<T> reconstructmatrix<M,T>(const M &implicitmat)}{Explicit matrix of an implicit one (applying \code{gemv} to unit vectors), for debugging; \lafunc{NRMat<T> reconstructmatrix<M,T>(const M &implicitmat)}{Explicit matrix of an implicit one (applying \code{gemv} to unit vectors), for debugging;
both template arguments must be given. Equivalent to \code{explicit_matrix<T>} of \header{davidson.h} (chapter~\ref{ch:iterative}).} both template arguments must be given. Equivalent to \code{explicit_matrix<T>} of \header{davidson.h} (chapter~\ref{ch:iterative}).}
\lafunc{void xcopy(int n, const T *x, int incx, T *y, int incy); void xaxpy(int n, const T &a, const T *x, int incx, T *y, int incy); T xdot(int n, const T *x, int incx, const T *y, int incy)}{Type-generic \lafunc{void xcopy(int n, const T *x, int incx, T *y, int incy); void xaxpy(int n, const T &a, const T *x, int incx, T *y, int incy); T xdot(int n, const T *x, int incx, const T *y, int incy)}{Type-generic
names of the CBLAS routines (to ease switching between \code{float}/\code{double}/complex in user code); at present specialized only for \code{double}.} names of the CBLAS routines (to ease switching between \code{float}/\code{double}/complex in user code); specialized for \code{double} and \code{float}.}
\end{lareference} \end{lareference}
\section{Examples}\label{sec:nonclass:examples} \section{Examples}\label{sec:nonclass:examples}
+35 -8
View File
@@ -23,12 +23,13 @@ Methods are written on top of a small infrastructure (evaluation counter, iterat
common drivers (line search, first order, trust region), so that a new method usually consists of a short class supplying the common drivers (line search, first order, trust region), so that a new method usually consists of a short class supplying the
search direction, the update rule or the trust-region step (section~\ref{sec:optimize:drivers}). search direction, the update rule or the trust-region step (section~\ref{sec:optimize:drivers}).
All templates are parameterized by the real type \code{T}, but optimize.cc instantiates them for \code{T = double} only. All templates are parameterized by the real type \code{T}; optimize.cc instantiates them for \code{T = double} and
The capability interfaces, adapters and the hyperparameter classes are header-only templates; the optimizers, the adapters with \code{float}. The capability interfaces, adapters and the hyperparameter classes are header-only templates; the optimizers, the
member functions in optimize.cc (\code{FrozenOptProblem}, \code{ParameterShift}, \code{FiniteDifference}, adapters with member functions in optimize.cc (\code{FrozenOptProblem}, \code{ParameterShift}, \code{FiniteDifference},
\code{OptLeastSquares}) and the auxiliary functions exist in the library only for \code{double}. \code{OptLeastSquares}) and the auxiliary functions exist in the library for \code{double} and \code{float}.
The common drivers \code{linesearch_minimize}, \code{firstorder_minimize} and \code{trustregion_minimize}, the extension The common drivers \code{linesearch_minimize}, \code{firstorder_minimize} and \code{trustregion_minimize}, the extension
points for user-written methods, are explicitly instantiated for \code{double} as well. points for user-written methods, are explicitly instantiated for both types as well. The defaults depending on the precision
are taken from \code{opt_precision<T>} (section~\ref{sec:optimize:hyper}).
The data members of all classes of this chapter are small, except vectors held by \code{FrozenOptProblem} and The data members of all classes of this chapter are small, except vectors held by \code{FrozenOptProblem} and
\code{optevaluator}; these are ordinary \code{NRVec} objects following the reference counting and copy-on-demand scheme of \code{optevaluator}; these are ordinary \code{NRVec} objects following the reference counting and copy-on-demand scheme of
@@ -254,6 +255,29 @@ hyperparameters maxstep(200) maxfunc(0) f_thr(0) x_thr(0) sad
+- Cobyla_hyperparameters rhobeg(0.5) rhoend(1e-6) flexible_radius(true); maxstep=100000 +- Cobyla_hyperparameters rhobeg(0.5) rhoend(1e-6) flexible_radius(true); maxstep=100000
\end{lstlisting} \end{lstlisting}
The defaults given above and below are those for \code{double}. The precision-dependent ones are taken from the trait
\code{opt_precision<T>} (\header{optimize.h}, analogous to \code{LA_precision<T>} of section~\ref{sec:concepts:traits}), defined
for \code{T = double} and \code{float} only; the double values are those used by the library before. In float the gradient norm
reachable by a line search is limited by the resolution of $f$ (about $\epsilon|f|$ divided by the step), hence the larger
\code{g_thr}.
\begin{center}
\begin{tabular}{llll}
\toprule
member & double & float & default of \\
\midrule
\code{fd_central_step()} & $10^{-6}$ & $5\cdot10^{-3}$ & \code{hh} of \code{FiniteDifference}, \code{ParameterShift} \\
\code{fd_forward_step()} & $10^{-7}$ & $3\cdot10^{-4}$ & \code{h} of \code{OptLeastSquares}, \code{my_fd_step} (LM) \\
\code{g_thr()} & $10^{-6}$ & $10^{-3}$ & \code{my_g_thr} \\
\code{sy_thr()} & $10^{-12}$ & $10^{-6}$ & \code{my_sy_thr} (quasi-Newton) \\
\code{sr1_thr()} & $10^{-8}$ & $10^{-4}$ & \code{my_sr1_thr} (SR1) \\
\code{update_thr()} & $10^{-8}$ & $10^{-4}$ & \code{my_update_thr} (trust region) \\
\code{nm_f_thr()}, \code{nm_x_thr()} & $10^{-8}$, $10^{-6}$ & $10^{-5}$, $10^{-4}$ & \code{my_f_thr}, \code{my_x_thr} (Nelder-Mead) \\
\code{powell_f_thr()}, \code{powell_x_thr()} & $10^{-10}$, $10^{-6}$ & $10^{-6}$, $10^{-4}$ & \code{my_f_thr}, \code{my_x_thr} (Powell) \\
\code{rhoend()} & $10^{-6}$ & $10^{-4}$ & \code{my_rhoend} (COBYLA) \\
\bottomrule
\end{tabular}
\end{center}
\paragraph{Base classes.} \paragraph{Base classes.}
\begin{lareference} \begin{lareference}
\lafunc{template<typename T> class hyperparameters}{Parameters common to all methods.} \lafunc{template<typename T> class hyperparameters}{Parameters common to all methods.}
@@ -671,16 +695,19 @@ saddle order.}
For least-squares problems providing \code{OptResiduals} (typically derived from \code{OptLeastSquares}): the step $h$ solves For least-squares problems providing \code{OptResiduals} (typically derived from \code{OptLeastSquares}): the step $h$ solves
$(J\Tr J+\mu D)h=-J\Tr r$ (\code{linear_solve}), $D=I$ or $D=\mathrm{diag}(J\Tr J)$ (Marquardt scaling); the damping starts at $(J\Tr J+\mu D)h=-J\Tr r$ (\code{linear_solve}), $D=I$ or $D=\mathrm{diag}(J\Tr J)$ (Marquardt scaling); the damping starts at
$\mu=\tau\max_i(J\Tr J)_{ii}$ and is updated by the gain ratio $\varrho$ \cite{optimize:madsen2004} (alg.~3.16): for $\varrho>0$ $\mu=\tau\max_i(J\Tr J)_{ii}$ (with scaling $\mu=\tau$, the same relative to $D$) and is updated by the gain ratio $\varrho$ \cite{optimize:madsen2004} (alg.~3.16): for $\varrho>0$
the step is accepted and $\mu\leftarrow\mu\max(1/3,1-(2\varrho-1)^3)$, $\nu=2$, otherwise $\mu\leftarrow\mu\nu$, $\nu\leftarrow2\nu$. the step is accepted and $\mu\leftarrow\mu\max(1/3,1-(2\varrho-1)^3)$, $\nu=2$, otherwise $\mu\leftarrow\mu\nu$, $\nu\leftarrow2\nu$.
The Jacobian comes from \code{jacobian()} of the problem or from forward differences (relative step \code{my_fd_step}, $n$ The Jacobian comes from \code{jacobian()} of the problem or from forward differences (relative step \code{my_fd_step}, $n$
residual evaluations counted in \code{my_maxfunc}). Convergence: $|J\Tr r|<$ \code{my_g_thr}, the general $f$/$x$ criterion, or residual evaluations counted in \code{my_maxfunc}). Convergence: $|J\Tr r|<$ \code{my_g_thr}, the general $f$/$x$ criterion, or
when a rejected step becomes negligible at machine precision ($|h|\le10\epsilon(|x|+\epsilon)$). The function value returned is when a rejected step becomes negligible at machine precision ($h_i^2\le(10\epsilon)^2(x_i^2+\epsilon^2)$ for all $i$,
tested per component to be scale invariant, cf.\ \cite{optimize:madsen2004} eq.~3.16). The function value returned is
$f=\frac12|r|^2$. The problem's \code{funcgrad()} is not used. $f=\frac12|r|^2$. The problem's \code{funcgrad()} is not used.
\begin{lareference} \begin{lareference}
\lafunc{template<typename T> class LM_hyperparameters : public gradient_hyperparameters<T>}{} \lafunc{template<typename T> class LM_hyperparameters : public gradient_hyperparameters<T>}{}
\lafunc{T my_tau}{Initial damping factor $\tau$ (\code{1e-3}).} \lafunc{T my_tau}{Initial damping factor $\tau$ (\code{1e-3}).}
\lafunc{bool my_scaling}{Marquardt scaling $D=\mathrm{diag}(J\Tr J)$ (default \code{false}, $D=I$).} \lafunc{bool my_scaling}{Marquardt scaling $D=\mathrm{diag}(J\Tr J)$ (default \code{false}, $D=I$). Recommended for
badly scaled parameters, in float usually necessary (otherwise the steps in the weakly curved directions fall below the
resolution of $f$).}
\lafunc{T my_fd_step}{Relative step of the forward-difference Jacobian (\code{1e-7}).} \lafunc{T my_fd_step}{Relative step of the forward-difference Jacobian (\code{1e-7}).}
\lafunc{template<typename T> class LM_Optimizer : public Optimizer<T>}{\code{laerror} if the problem does not provide \lafunc{template<typename T> class LM_Optimizer : public Optimizer<T>}{\code{laerror} if the problem does not provide
\code{OptResiduals}. Verbose name Levenberg-Marquardt.} \code{OptResiduals}. Verbose name Levenberg-Marquardt.}
+5 -5
View File
@@ -10,7 +10,7 @@ This chapter describes the polynomial class \code{Polynomial<T>} and the exact r
\code{template <typename T> class Polynomial : public NRVec<T>} represents $p(x)=\sum_{i=0}^{d}c_ix^i$; the coefficient $c_i$ is the vector element \code{template <typename T> class Polynomial : public NRVec<T>} represents $p(x)=\sum_{i=0}^{d}c_ix^i$; the coefficient $c_i$ is the vector element
\code{p[i]} (indexed from 0, i.e.\ by the power) and the vector has $d+1$ elements. The member \code{size()} is made private to prevent confusion; use \code{p[i]} (indexed from 0, i.e.\ by the power) and the vector has $d+1$ elements. The member \code{size()} is made private to prevent confusion; use
\code{degree()}. The leading coefficient may be zero (e.g.\ after a subtraction); \code{simplify()} removes such terms. The class is explicitly \code{degree()}. The leading coefficient may be zero (e.g.\ after a subtraction); \code{simplify()} removes such terms. The class is explicitly
instantiated for \code{int}, \code{unsigned int}, \code{double}, \code{std::complex<double>}; root finding and SVD-based gcd require a floating point type. instantiated for \code{int}, \code{unsigned int}, \code{double}, \code{std::complex<double>}, \code{float}, \code{std::complex<float>}; root finding and SVD-based gcd require a floating point type.
All inherited \code{NRVec} functionality (norms, \code{randomize}, \code{clear}, I/O operators printing the number of coefficients followed by the All inherited \code{NRVec} functionality (norms, \code{randomize}, \code{clear}, I/O operators printing the number of coefficients followed by the
coefficients, \ldots) is available. \code{Polynomial} follows the copy-on-demand scheme of section~\ref{sec:concepts:refcount}: its modifying members call coefficients, \ldots) is available. \code{Polynomial} follows the copy-on-demand scheme of section~\ref{sec:concepts:refcount}: its modifying members call
@@ -77,10 +77,10 @@ vector, hence also a \code{Polynomial} (section~\ref{sec:nonclass:matexp}).
\subsection{Roots, interpolation, gcd} \subsection{Roots, interpolation, gcd}
\begin{lareference} \begin{lareference}
\lafunc{NRMat<T> companion() const}{companion matrix ($d\times d$, ones on the superdiagonal, last row $-c_i/c_d$) whose characteristic polynomial is $p/c_d$.} \lafunc{NRMat<T> companion() const}{companion matrix ($d\times d$, ones on the superdiagonal, last row $-c_i/c_d$) whose characteristic polynomial is $p/c_d$.}
\lafunc{NRVec<typename LA_traits<T>::complextype> roots() const}{all roots as eigenvalues of the companion matrix (\code{gdiagonalize}, chapter~\ref{ch:nonclass}); for \code{double} \lafunc{NRVec<typename LA_traits<T>::complextype> roots() const}{all roots as eigenvalues of the companion matrix (\code{gdiagonalize}, chapter~\ref{ch:nonclass}); for the floating point types
and \code{std::complex<double>} only (laerror for integer types).} \code{double}, \code{float}, \code{std::complex<double>} and \code{std::complex<float>} only (laerror for integer types).}
\lafunc{NRVec<T> realroots(const typename LA_traits<T>::normtype thr) const}{roots with $|\mathrm{Im}|<$ \code{thr}, real parts sorted ascending (\code{T = double}).} \lafunc{NRVec<T> realroots(const typename LA_traits<T>::normtype thr) const}{roots with $|\mathrm{Im}|<$ \code{thr}, real parts sorted ascending (floating point types).}
\lafunc{T newton(const T x0, const typename LA_traits<T>::normtype thr=1e-14, const int maxit=1000) const}{Newton iteration from \code{x0} until $|p(x)|<$ \code{thr}; no convergence report.} \lafunc{T newton(const T x0, const typename LA_traits<T>::normtype thr=LA_precision<normtype>::newton_threshold(), const int maxit=1000) const}{Newton iteration from \code{x0} until $|p(x)|<$ \code{thr}; no convergence report.}
\lafunc{template <typename T> Polynomial<T> polyfromroots(const NRVec<T> &roots, const int skip= -1)}{monic $\prod_{i\ne\mathrm{skip}}(x-r_i)$.} \lafunc{template <typename T> Polynomial<T> polyfromroots(const NRVec<T> &roots, const int skip= -1)}{monic $\prod_{i\ne\mathrm{skip}}(x-r_i)$.}
\lafunc{template <typename T> Polynomial<T> lagrange_interpolation(const NRVec<T> &x, const NRVec<T> &y)}{interpolating polynomial of degree $n-1$ through the $n$ points; laerror for repeated $x$.} \lafunc{template <typename T> Polynomial<T> lagrange_interpolation(const NRVec<T> &x, const NRVec<T> &y)}{interpolating polynomial of degree $n-1$ through the $n$ points; laerror for repeated $x$.}
\lafunc{template <typename T> NRMat<T> Sylvester(const Polynomial<T> &p, const Polynomial<T> &q)}{Sylvester matrix of dimension $\deg p+\deg q$ (its determinant is the resultant).} \lafunc{template <typename T> NRMat<T> Sylvester(const Polynomial<T> &p, const Polynomial<T> &q)}{Sylvester matrix of dimension $\deg p+\deg q$ (its determinant is the resultant).}
+10 -11
View File
@@ -14,11 +14,10 @@ matrices) are returned as \code{NRMat<T>}.
\code{NRSMat_from1<T>} (section~\ref{sec:smat:from1}) is the same class with the \code{NRSMat_from1<T>} (section~\ref{sec:smat:from1}) is the same class with the
two-index \code{operator()} counting from 1. two-index \code{operator()} counting from 1.
The file \code{smat.cc} contains explicit instantiations for the element types \code{double}, The file \code{smat.cc} contains explicit instantiations for the element types \code{double}, \code{float},
\code{std::complex<double>} and the integer types \code{char}, \code{short}, \code{int}, \code{long}, \code{long long} and \code{std::complex<double>}, \code{std::complex<float>} and the integer types \code{char}, \code{short}, \code{int}, \code{long}, \code{long long} and
their unsigned variants (\code{float} is not instantiated). their unsigned variants.
Most numerical members have BLAS-based specializations only for the two types \code{double} and Most numerical members have BLAS-based specializations only for the four floating point types. Some members are defined only for these types:
\code{std::complex<double>}. Some members are defined only for these two types:
\code{amax}, \code{amin}, \code{norm}, \code{dot}, \code{axpy}, \code{randomize}, \code{inverse} \code{amax}, \code{amin}, \code{norm}, \code{dot}, \code{axpy}, \code{randomize}, \code{inverse}
and the products with \code{NRMat} and \code{NRSMat}. and the products with \code{NRMat} and \code{NRSMat}.
@@ -105,10 +104,10 @@ are CPU only (\code{NOT_GPU}).
\lafunc{NRSMat(const T &a, const int n)}{Order $n$, all $n(n+1)/2$ stored elements (i.e.\ also the off-diagonal ones) set to \code{a}.} \lafunc{NRSMat(const T &a, const int n)}{Order $n$, all $n(n+1)/2$ stored elements (i.e.\ also the off-diagonal ones) set to \code{a}.}
\lafunc{NRSMat(const T *a, const int n)}{Copy of $n(n+1)/2$ packed elements (lower triangle by rows) from \code{a}.} \lafunc{NRSMat(const T *a, const int n)}{Copy of $n(n+1)/2$ packed elements (lower triangle by rows) from \code{a}.}
\lafunc{NRSMat(const NRSMat &rhs)}{Shallow copy.} \lafunc{NRSMat(const NRSMat &rhs)}{Shallow copy.}
\lafunc{NRSMat(const typename LA_traits_complex<T>::NRSMat_Noncomplex_type &rhs, bool imagpart = false)}{Complex matrix from a real one: \code{rhs} becomes the real part (\code{imagpart=false}) or the imaginary part (\code{imagpart=true}), the other part is zero. Defined only for \code{T=std::complex<double>}.} \lafunc{NRSMat(const typename LA_traits_complex<T>::NRSMat_Noncomplex_type &rhs, bool imagpart = false)}{Complex matrix from a real one: \code{rhs} becomes the real part (\code{imagpart=false}) or the imaginary part (\code{imagpart=true}), the other part is zero. Defined only for \code{T=std::complex<double>} and \code{std::complex<float>}.}
\lafunc{explicit NRSMat(const NRMat<T> &rhs)}{Symmetric part $(A+A\Tr)/2$ of a square dense matrix (deep copy; nonsquare input is checked only in DEBUG).} \lafunc{explicit NRSMat(const NRMat<T> &rhs)}{Symmetric part $(A+A\Tr)/2$ of a square dense matrix (deep copy; nonsquare input is checked only in DEBUG).}
\lafunc{explicit NRSMat(const NRVec<T> &rhs, const int n)}{Interprets a vector of length $n(n+1)/2$ as packed matrix of order $n$. \emph{Shares} the data with \code{rhs} (no copy); length checked only in DEBUG.} \lafunc{explicit NRSMat(const NRVec<T> &rhs, const int n)}{Interprets a vector of length $n(n+1)/2$ as packed matrix of order $n$. \emph{Shares} the data with \code{rhs} (no copy); length checked only in DEBUG.}
\lafunc{explicit NRSMat(const SparseMat<T> &rhs); explicit NRSMat(const SparseSMat<T> &rhs)}{Dense from sparse, see \ref{ch:sparse}.} \lafunc{explicit NRSMat(const SparseMat<T> &rhs); explicit NRSMat(const SparseSMat<T> &rhs); explicit NRSMat(const CSRMat<T> &rhs)}{Dense from sparse, see \ref{ch:sparse}; from \code{SparseSMat} and \code{CSRMat} the lower triangle is taken.}
\lafunc{~NRSMat()}{Destructor, frees the data when the last reference disappears.} \lafunc{~NRSMat()}{Destructor, frees the data when the last reference disappears.}
\lafunc{NRSMat & operator=(const NRSMat &rhs)}{Shallow copy, see section~\ref{sec:concepts:assign}.} \lafunc{NRSMat & operator=(const NRSMat &rhs)}{Shallow copy, see section~\ref{sec:concepts:assign}.}
\lafunc{NRSMat & operator|=(const NRSMat &rhs)}{Deep copy.} \lafunc{NRSMat & operator|=(const NRSMat &rhs)}{Deep copy.}
@@ -161,20 +160,20 @@ generated from the in-place ones.
\lafunc{void gemv(const T beta, NRVec<std::complex<T> > &r, const char trans, const T alpha, const NRVec<std::complex<T> > &x) const}{The same, real matrix and complex vectors.} \lafunc{void gemv(const T beta, NRVec<std::complex<T> > &r, const char trans, const T alpha, const NRVec<std::complex<T> > &x) const}{The same, real matrix and complex vectors.}
\lafunc{void axpy(const T alpha, const NRSMat &x)}{$S \leftarrow S + \alpha x$. For hermitian complex matrices the result is hermitian only for real $\alpha$ \lafunc{void axpy(const T alpha, const NRSMat &x)}{$S \leftarrow S + \alpha x$. For hermitian complex matrices the result is hermitian only for real $\alpha$
(not checked).} (not checked).}
\lafunc{void rank2update(const T alpha, const NRVec<T> &x, const NRVec<T> &y)}{Rank-2 update $S \leftarrow S + \alpha x y\Hc + \bar\alpha y x\Hc$ (real: $\alpha(xy\Tr+yx\Tr)$); \code{dspr2}/\code{zhpr2} for \code{double}/\code{complex<double>}, generic loop otherwise. A symmetric rank-1 update $S \leftarrow S + \beta x x\Hc$ (real $\beta$) is obtained as \code{rank2update(beta/2,x,x)}; there is no separate rank-1 member.} \lafunc{void rank2update(const T alpha, const NRVec<T> &x, const NRVec<T> &y)}{Rank-2 update $S \leftarrow S + \alpha x y\Hc + \bar\alpha y x\Hc$ (real: $\alpha(xy\Tr+yx\Tr)$); \code{dspr2}/\code{zhpr2}/\code{sspr2}/\code{chpr2} for \code{double}/\code{complex<double>}/\code{float}/\code{complex<float>}, generic loop otherwise. A symmetric rank-1 update $S \leftarrow S + \beta x x\Hc$ (real $\beta$) is obtained as \code{rank2update(beta/2,x,x)}; there is no separate rank-1 member.}
\lafunc{const T dot(const NRSMat &rhs) const}{$\sum_{i\ge j} \bar A_{ij} B_{ij}$, i.e.\ the dot product of the \emph{packed} vectors (off-diagonal elements counted once; \code{ddot}/\code{zdotc}).} \lafunc{const T dot(const NRSMat &rhs) const}{$\sum_{i\ge j} \bar A_{ij} B_{ij}$, i.e.\ the dot product of the \emph{packed} vectors (off-diagonal elements counted once; \code{ddot}/\code{zdotc}).}
\lafunc{const T dot(const NRVec<T> &rhs) const}{Dot product of the packed data with a vector of length $n(n+1)/2$.} \lafunc{const T dot(const NRVec<T> &rhs) const}{Dot product of the packed data with a vector of length $n(n+1)/2$.}
\lafunc{const typename LA_traits<T>::normtype norm(const T scalar = (T)0) const}{Euclidean norm of the packed data of $S - \mathtt{scalar}\cdot 1$; off-diagonal elements are counted \emph{once}, so this is not the Frobenius norm of the full matrix.} \lafunc{const typename LA_traits<T>::normtype norm(const T scalar = (T)0) const}{Euclidean norm of the packed data of $S - \mathtt{scalar}\cdot 1$; off-diagonal elements are counted \emph{once}, so this is not the Frobenius norm of the full matrix.}
\lafunc{const T trace() const}{Trace.} \lafunc{const T trace() const}{Trace.}
\lafunc{const T sum(bool onlytriangle=false) const}{Sum of all elements of the full matrix, or of the stored triangle only.} \lafunc{const T sum(bool onlytriangle=false) const}{Sum of all elements of the full matrix, or of the stored triangle only.}
\lafunc{const NRVec<T> rsum() const; const NRVec<T> csum() const}{Row sums and column sums of the full matrix (equal for real, complex conjugate of each other for hermitian matrices).} \lafunc{const NRVec<T> rsum() const; const NRVec<T> csum() const}{Row sums and column sums of the full matrix (equal for real, complex conjugate of each other for hermitian matrices).}
\lafunc{inline const T amax() const; inline const T amin() const}{Stored element with the largest/smallest absolute value (complex: $|\Re|+|\Im|$); \code{double} and \code{complex<double>} only.} \lafunc{inline const T amax() const; inline const T amin() const}{Stored element with the largest/smallest absolute value (complex: $|\Re|+|\Im|$); floating point types only.}
\lafunc{const T* diagonalof(NRVec<T> &r, const bool divide = 0, bool cache = false) const}{\code{divide=false}: store the diagonal into \code{r} and return \code{&r[0]}; \code{divide=true}: divide \code{r} elementwise by the nonzero diagonal elements and return \code{NULL}. \code{cache} is unused. Interface used by iterative solvers (\ref{ch:iterative}).} \lafunc{const T* diagonalof(NRVec<T> &r, const bool divide = 0, bool cache = false) const}{\code{divide=false}: store the diagonal into \code{r} and return \code{&r[0]}; \code{divide=true}: divide \code{r} elementwise by the nonzero diagonal elements and return \code{NULL}. \code{cache} is unused. Interface used by iterative solvers (\ref{ch:iterative}).}
\lafunc{NRSMat inverse()}{Inverse via \code{NRMat<T>(*this).inverse()}, symmetrized back. \code{double} and \code{complex<double>} only.} \lafunc{NRSMat inverse()}{Inverse via \code{NRMat<T>(*this).inverse()}, symmetrized back. Floating point types only.}
\lafunc{NRSMat& conjugateme(); const NRSMat conjugate() const}{Complex conjugation of all stored elements (no-op for real types).} \lafunc{NRSMat& conjugateme(); const NRSMat conjugate() const}{Complex conjugation of all stored elements (no-op for real types).}
\lafunc{NRSMat<typename LA_traits<T>::normtype> abs() const}{Elementwise absolute values.} \lafunc{NRSMat<typename LA_traits<T>::normtype> abs() const}{Elementwise absolute values.}
\lafunc{NRSMat<typename LA_traits<T>::normtype> diffabs(const NRSMat &rhs) const}{Elementwise $|A_{ij}|-|B_{ij}|$.} \lafunc{NRSMat<typename LA_traits<T>::normtype> diffabs(const NRSMat &rhs) const}{Elementwise $|A_{ij}|-|B_{ij}|$.}
\lafunc{void randomize(const typename LA_traits<T>::normtype &x)}{Uniformly distributed elements in $(-x,x)$; complex: real and imaginary parts independent, imaginary part of the diagonal set to zero. \code{double} and \code{complex<double>} only.} \lafunc{void randomize(const typename LA_traits<T>::normtype &x)}{Uniformly distributed elements in $(-x,x)$; complex: real and imaginary parts independent, imaginary part of the diagonal set to zero. Floating point types only.}
\lafunc{const bool operator==(const NRSMat &rhs) const; const bool operator!=(const NRSMat &rhs) const}{Exact elementwise comparison.} \lafunc{const bool operator==(const NRSMat &rhs) const; const bool operator!=(const NRSMat &rhs) const}{Exact elementwise comparison.}
\end{lareference} \end{lareference}
+129 -25
View File
@@ -1,8 +1,8 @@
\chapter{Sparse matrices}\label{ch:sparse} \chapter{Sparse matrices}\label{ch:sparse}
\laheader{sparsemat.h, sparsemat.cc, sparsesmat.h, sparsesmat.cc} \laheader{sparsemat.h, sparsemat.cc, sparsesmat.h, sparsesmat.cc, csrmat.h, csrmat.cc}
\section{Overview}\label{sec:sparse:overview} \section{Overview}\label{sec:sparse:overview}
\LA{} offers two independent sparse matrix classes with different storage schemes, plus a small helper class: \LA{} offers three independent sparse matrix classes with different storage schemes, plus a small helper class:
\begin{itemize} \begin{itemize}
\item \code{SparseMat<T>} (section~\ref{sec:sparse:sparsemat}): an \emph{unordered linked list} of \item \code{SparseMat<T>} (section~\ref{sec:sparse:sparsemat}): an \emph{unordered linked list} of
(row, column, value) triplets. Appending elements is cheap, duplicates are allowed and summed, (row, column, value) triplets. Appending elements is cheap, duplicates are allowed and summed,
@@ -15,19 +15,21 @@
exponentiating symmetric matrices (e.g.\ the unitary complex symmetric $\exp(\mathrm{i}H)$ with real symmetric $H$), exponentiating symmetric matrices (e.g.\ the unitary complex symmetric $\exp(\mathrm{i}H)$ with real symmetric $H$),
but despite its name it is a general (also nonsymmetric and rectangular) sparse matrix; symmetry but despite its name it is a general (also nonsymmetric and rectangular) sparse matrix; symmetry
(hermiticity for complex types) is only a convention of some of its operations. (hermiticity for complex types) is only a convention of some of its operations.
\item \code{CSRMat<T>} (section~\ref{sec:sparse:csr}): the compressed sparse row format (row pointers, column
indices, values), read-efficient for products with vectors and matrices, e.g.\ in iterative solvers; built by
conversion from the other classes.
\item \code{SemiSparseMat<T>} (section~\ref{sec:sparse:semisparse}): a dense diagonal plus a \item \code{SemiSparseMat<T>} (section~\ref{sec:sparse:semisparse}): a dense diagonal plus a
\code{SparseMat} off-diagonal part, just enough to be used in iterative eigensolvers. \code{SparseMat} off-diagonal part, just enough to be used in iterative eigensolvers.
\end{itemize} \end{itemize}
\code{CSRMat<T>} (section~\ref{sec:sparse:csr}) is unfinished.
Common definitions in \code{sparsemat.h}: Common definitions in \code{sparsemat.h}:
\begin{lareference} \begin{lareference}
\lafunc{typedef unsigned int SPMatindex; typedef int SPMatindexdiff}{Index type and index difference type. All row and column indices count from 0.} \lafunc{typedef unsigned int SPMatindex; typedef int SPMatindexdiff}{Index type and index difference type. All row and column indices count from 0.}
\lafunc{const double SPARSEEPSILON=1e-15}{Threshold: elements with absolute value below (or equal to) it are dropped by \code{simplify()} and by most constructors and accumulating operations.} \lafunc{const double SPARSEEPSILON=1e-15}{Kept for user code. The library itself uses the precision-dependent threshold \code{LA_precision<normtype>::sparse_epsilon()} (section~\ref{sec:concepts:traits}; $10^{-15}$ for double, i.e.\ $\epsilon_{\rm sp}$, and $10^{-7}$ for float): elements with absolute value below (or equal to) it are dropped by \code{simplify()} and by most constructors and accumulating operations; it is denoted $\epsilon_{\rm sp}$ below.}
\lafunc{template<typename T> struct matel {T elem; SPMatindex row; SPMatindex col; matel *next;}}{List element of \code{SparseMat}; also the value type returned by the iterators of both classes.} \lafunc{template<typename T> struct matel {T elem; SPMatindex row; SPMatindex col; matel *next;}}{List element of \code{SparseMat}; also the value type returned by the iterators of both classes.}
\end{lareference} \end{lareference}
Both classes are explicitly instantiated for \code{double} and \code{std::complex<double>}. Both classes are explicitly instantiated for \code{double}, \code{float}, \code{std::complex<double>} and \code{std::complex<float>}.
Several operations are implemented for \code{double} only. \code{SparseMat} nowhere conjugates when Several operations are implemented for the real types \code{double} and \code{float} only. \code{SparseMat} nowhere conjugates when
it mirrors elements of a symmetric matrix (source comments: ``not OK for complex hermitean''), so it mirrors elements of a symmetric matrix (source comments: ``not OK for complex hermitean''), so
its complex symmetric matrices are complex symmetric, not hermitian. \code{SparseSMat} stores both triangles its complex symmetric matrices are complex symmetric, not hermitian. \code{SparseSMat} stores both triangles
and its mirroring operations follow the hermitian convention of \code{NRSMat} (section~\ref{sec:smat:hermitian}). and its mirroring operations follow the hermitian convention of \code{NRSMat} (section~\ref{sec:smat:hermitian}).
@@ -43,7 +45,7 @@ which no longer exists, is not included by any file and is not installed; it is
The nonzero elements form a singly linked list of \code{matel<T>} (pointer \code{list}); new elements are The nonzero elements form a singly linked list of \code{matel<T>} (pointer \code{list}); new elements are
prepended. The list is in no particular order and may contain several entries with the same prepended. The list is in no particular order and may contain several entries with the same
$(i,j)$, which represent their sum; \code{simplify()} merges such duplicates, removes elements with $(i,j)$, which represent their sum; \code{simplify()} merges such duplicates, removes elements with
$|a_{ij}|\le$ \code{SPARSEEPSILON} and leaves the list sorted by rows. Operations needing an ordering $|a_{ij}|\le$ $\epsilon_{\rm sp}$ and leaves the list sorted by rows. Operations needing an ordering
(\code{simplify}, sparse products, \code{dot}) build private arrays of pointers sorted by rows or by columns (\code{simplify}, sparse products, \code{dot}) build private arrays of pointers sorted by rows or by columns
(member \code{sort()}); these arrays and the cached element count \code{nonzero} are kept until the next (member \code{sort()}); these arrays and the cached element count \code{nonzero} are kept until the next
operation which calls the private \code{unsort()}: \code{add()} (and everything built on it), \code{simplify}, operation which calls the private \code{unsort()}: \code{add()} (and everything built on it), \code{simplify},
@@ -86,8 +88,9 @@ Most modifying members call \code{copyonwrite()}: \code{simplify}, \code{operato
\lafunc{SparseMat()}{Undefined empty matrix ($0\times0$, no reference count).} \lafunc{SparseMat()}{Undefined empty matrix ($0\times0$, no reference count).}
\lafunc{SparseMat(const SPMatindex n, const SPMatindex m)}{Defined empty (all zero) $n\times m$ matrix, not symmetric.} \lafunc{SparseMat(const SPMatindex n, const SPMatindex m)}{Defined empty (all zero) $n\times m$ matrix, not symmetric.}
\lafunc{SparseMat(const SparseMat &rhs)}{Shallow copy; an empty matrix gets its own reference count.} \lafunc{SparseMat(const SparseMat &rhs)}{Shallow copy; an empty matrix gets its own reference count.}
\lafunc{explicit SparseMat(const NRMat<T> &rhs)}{From a dense matrix, elements with $|a_{ij}|>$ \code{SPARSEEPSILON}.} \lafunc{explicit SparseMat(const NRMat<T> &rhs)}{From a dense matrix, elements with $|a_{ij}|>$ $\epsilon_{\rm sp}$.}
\lafunc{explicit SparseMat(const NRSMat<T> &rhs)}{From a packed symmetric matrix; symmetric flag set, lower triangle stored.} \lafunc{explicit SparseMat(const NRSMat<T> &rhs)}{From a packed symmetric matrix; symmetric flag set, lower triangle stored.}
\lafunc{explicit SparseMat(const CSRMat<T> &rhs)}{From a \code{CSRMat} (section~\ref{sec:sparse:csr}): all stored elements, not symmetric.}
\lafunc{~SparseMat()}{Destructor.} \lafunc{~SparseMat()}{Destructor.}
\lafunc{SparseMat & operator=(const SparseMat &rhs)}{Shallow copy (also copies the symmetric flag).} \lafunc{SparseMat & operator=(const SparseMat &rhs)}{Shallow copy (also copies the symmetric flag).}
\lafunc{SparseMat & operator=(const T &a)}{Scalar matrix $a\cdot1$; the matrix must be square and dimensioned, old contents are discarded.} \lafunc{SparseMat & operator=(const T &a)}{Scalar matrix $a\cdot1$; the matrix must be square and dimensioned, old contents are discarded.}
@@ -107,8 +110,8 @@ duplicates summed), \code{explicit NRSMat<T>(const SparseMat<T>&)} (requires the
\subsection{Element insertion and access} \subsection{Element insertion and access}
\begin{lareference} \begin{lareference}
\lafunc{inline void add(const SPMatindex n, const SPMatindex m, const T elem)}{Prepend element $(n,m)$; it is summed with existing entries at the same position. No checks, no \code{copyonwrite()}, see section~\ref{sec:sparse:refcount}. The matrix must have been dimensioned (constructor with dimensions or \code{resize}).} \lafunc{inline void add(const SPMatindex n, const SPMatindex m, const T elem)}{Prepend element $(n,m)$; it is summed with existing entries at the same position. No checks, no \code{copyonwrite()}, see section~\ref{sec:sparse:refcount}. The matrix must have been dimensioned (constructor with dimensions or \code{resize}).}
\lafunc{void addsafe(const SPMatindex n, const SPMatindex m, const T elem)}{As \code{add}, but ignores $|$\code{elem}$|\le$ \code{SPARSEEPSILON}, calls \code{copyonwrite()} and creates the reference count of an undefined matrix (range check only with the lowercase \code{debug} macro).} \lafunc{void addsafe(const SPMatindex n, const SPMatindex m, const T elem)}{As \code{add}, but ignores $|$\code{elem}$|\le\epsilon_{\rm sp}$, calls \code{copyonwrite()} and creates the reference count of an undefined matrix (range check only with the lowercase \code{debug} macro).}
\lafunc{void simplify(const double sparseepsilon=SPARSEEPSILON)}{Sum duplicates, remove elements with $|a_{ij}|\le$ \code{sparseepsilon}, sort by rows; symmetric matrices are first moved to the upper triangle ($i\le j$).} \lafunc{void simplify(const typename LA_traits<T>::normtype sparseepsilon=LA_precision<normtype>::sparse_epsilon())}{Sum duplicates, remove elements with $|a_{ij}|\le$ \code{sparseepsilon}, sort by rows; symmetric matrices are first moved to the upper triangle ($i\le j$).}
\lafunc{unsigned int length() const}{Number of list elements (cached until the next modification, see section~\ref{sec:sparse:storage}).} \lafunc{unsigned int length() const}{Number of list elements (cached until the next modification, see section~\ref{sec:sparse:storage}).}
\lafunc{unsigned int sort(int type) const}{Build the internal array sorted by rows (\code{type=0}) or columns (\code{type=1}) and return the number of elements. Logically const, but unfolds a symmetric matrix (\code{setunsymmetric}). Normally called internally only.} \lafunc{unsigned int sort(int type) const}{Build the internal array sorted by rows (\code{type=0}) or columns (\code{type=1}) and return the number of elements. Logically const, but unfolds a symmetric matrix (\code{setunsymmetric}). Normally called internally only.}
\lafunc{inline matel<T> *getlist() const; void setlist(matel<T> *l)}{Raw access to the list head (no reference count handling; \code{setlist} does not free the old list).} \lafunc{inline matel<T> *getlist() const; void setlist(matel<T> *l)}{Raw access to the list head (no reference count handling; \code{setlist} does not free the old list).}
@@ -132,7 +135,7 @@ Binary operators with a matrix operand must not combine a symmetric left operand
\lafunc{const T trace() const}{Trace.} \lafunc{const T trace() const}{Trace.}
\lafunc{const typename LA_traits<T>::normtype norm(const T scalar=(T)0) const}{Frobenius norm of $A-\mathtt{scalar}\cdot1$ of the full matrix (symmetric off-diagonal elements counted twice). Calls \code{simplify()}.} \lafunc{const typename LA_traits<T>::normtype norm(const T scalar=(T)0) const}{Frobenius norm of $A-\mathtt{scalar}\cdot1$ of the full matrix (symmetric off-diagonal elements counted twice). Calls \code{simplify()}.}
\lafunc{const T dot(const SparseMat &rhs) const}{$\sum_{ij} a_{ij} b_{ij}$ without conjugation (also for complex types), general (not symmetric) matrices only; merge of the two lists sorted by rows.} \lafunc{const T dot(const SparseMat &rhs) const}{$\sum_{ij} a_{ij} b_{ij}$ without conjugation (also for complex types), general (not symmetric) matrices only; merge of the two lists sorted by rows.}
\lafunc{const T dot(const NRMat<T> &rhs) const}{$\sum_{ij} a_{ij} b_{ij}$ with a dense matrix; \code{double} only.} \lafunc{const T dot(const NRMat<T> &rhs) const}{$\sum_{ij} a_{ij} b_{ij}$ with a dense matrix; \code{double} and \code{float} only.}
\lafunc{const T* diagonalof(NRVec<T> &r, const bool divide=0, bool cache=false) const}{Square matrix: diagonal into \code{r} (\code{divide=false}, returns \code{&r[0]}) or divide \code{r} by the nonzero diagonal elements (\code{divide=true}, returns \code{NULL}). Rectangular matrix: the diagonal of $A\Tr A$ (assuming a simplified matrix), for preconditioning. \code{cache} is unused.} \lafunc{const T* diagonalof(NRVec<T> &r, const bool divide=0, bool cache=false) const}{Square matrix: diagonal into \code{r} (\code{divide=false}, returns \code{&r[0]}) or divide \code{r} by the nonzero diagonal elements (\code{divide=true}, returns \code{NULL}). Rectangular matrix: the diagonal of $A\Tr A$ (assuming a simplified matrix), for preconditioning. \code{cache} is unused.}
\end{lareference} \end{lareference}
@@ -140,7 +143,7 @@ Binary operators with a matrix operand must not combine a symmetric left operand
\begin{lareference} \begin{lareference}
\lafunc{inline const NRVec<T> operator*(const NRVec<T> &rhs) const}{$A x$.} \lafunc{inline const NRVec<T> operator*(const NRVec<T> &rhs) const}{$A x$.}
\lafunc{void gemv(const T beta, NRVec<T> &r, const char trans, const T alpha, const NRVec<T> &x, bool treat_as_symmetric=false) const}{$r \leftarrow \beta r + \alpha\,\mathrm{op}(A)x$; forwards to \code{NRVec<T>::gemv}. Any \code{trans} other than \code{'n'} means transposition (no conjugation). With \code{treat_as_symmetric} a general matrix is used as if it had the symmetric flag. The sizes of \code{x} and (in DEBUG) of the result are checked.} \lafunc{void gemv(const T beta, NRVec<T> &r, const char trans, const T alpha, const NRVec<T> &x, bool treat_as_symmetric=false) const}{$r \leftarrow \beta r + \alpha\,\mathrm{op}(A)x$; forwards to \code{NRVec<T>::gemv}. Any \code{trans} other than \code{'n'} means transposition (no conjugation). With \code{treat_as_symmetric} a general matrix is used as if it had the symmetric flag. The sizes of \code{x} and (in DEBUG) of the result are checked.}
\lafunc{inline const NRMat<T> operator*(const NRMat<T> &rhs) const}{$A B$ via \code{NRMat<T>::gemm} with a sparse first operand; \code{double} only (\code{complex<double>} raises ``not implemented yet'').} \lafunc{inline const NRMat<T> operator*(const NRMat<T> &rhs) const}{$A B$ via \code{NRMat<T>::gemm} with a sparse first operand; \code{double} and \code{float} only (the complex types raise ``not implemented yet'').}
\lafunc{const SparseMat operator*(const SparseMat &rhs) const}{Sparse product of two general matrices (error if one is symmetric); the result is simplified.} \lafunc{const SparseMat operator*(const SparseMat &rhs) const}{Sparse product of two general matrices (error if one is symmetric); the result is simplified.}
\lafunc{void gemm(const T beta, const SparseMat &a, const char transa, const SparseMat &b, const char transb, const T alpha)}{$C \leftarrow \beta C + \alpha\,\mathrm{op}(A)\,\mathrm{op}(B)$ for general \code{a}, \code{b}; if \code{*this} is symmetric (possible only with $\beta\neq0$) only elements with $i\le j$ are added. Result simplified. \code{a} and \code{b} must not be \code{*this} (checked in DEBUG).} \lafunc{void gemm(const T beta, const SparseMat &a, const char transa, const SparseMat &b, const char transb, const T alpha)}{$C \leftarrow \beta C + \alpha\,\mathrm{op}(A)\,\mathrm{op}(B)$ for general \code{a}, \code{b}; if \code{*this} is symmetric (possible only with $\beta\neq0$) only elements with $i\le j$ are added. Result simplified. \code{a} and \code{b} must not be \code{*this} (checked in DEBUG).}
\end{lareference} \end{lareference}
@@ -148,7 +151,7 @@ Related functions in other classes:
\code{NRVec<T>::gemv(const T beta, const SparseMat<T> &a, const char trans, const T alpha, const NRVec &x, const bool treat_as_symmetric=false)}, \code{NRVec<T>::gemv(const T beta, const SparseMat<T> &a, const char trans, const T alpha, const NRVec &x, const bool treat_as_symmetric=false)},
\code{NRVec<T>::operator*(const SparseMat<T>&)} ($x\Tr A$), \code{NRVec<T>::operator*(const SparseMat<T>&)} ($x\Tr A$),
\code{NRMat<T>::gemm(const T &beta, const SparseMat<T> &a, const char transa, const NRMat &b, const char transb, const T &alpha)} \code{NRMat<T>::gemm(const T &beta, const SparseMat<T> &a, const char transa, const NRMat &b, const char transb, const T &alpha)}
(\code{double} only) and \code{NRMat<T>::operator+=}, \code{operator-=(const SparseMat<T>&)}. (\code{double} and \code{float} only) and \code{NRMat<T>::operator+=}, \code{operator-=(const SparseMat<T>&)}.
\subsection{Transposition, permutations, direct sums} \subsection{Transposition, permutations, direct sums}
\begin{lareference} \begin{lareference}
@@ -180,7 +183,7 @@ e.g.\ \code{NRVec<SparseMat<T>>} can be written and read; the ORCA format is def
\lafunc{inline const SparseMat<T> commutator(const SparseMat<T> &x, const SparseMat<T> &y, const bool trx=0, const bool tryy=0)}{$[\mathrm{op}(x),\mathrm{op}(y)] = xy-yx$, via two \code{gemm} calls (general matrices only).} \lafunc{inline const SparseMat<T> commutator(const SparseMat<T> &x, const SparseMat<T> &y, const bool trx=0, const bool tryy=0)}{$[\mathrm{op}(x),\mathrm{op}(y)] = xy-yx$, via two \code{gemm} calls (general matrices only).}
\lafunc{inline const SparseMat<T> anticommutator(const SparseMat<T> &x, const SparseMat<T> &y, const bool trx=0, const bool tryy=0)}{$xy+yx$.} \lafunc{inline const SparseMat<T> anticommutator(const SparseMat<T> &x, const SparseMat<T> &y, const bool trx=0, const bool tryy=0)}{$xy+yx$.}
\end{lareference} \end{lareference}
In \ref{ch:nonclass}: \code{realpart()} and \code{imagpart()} of a \code{SparseMat<complex<double>>}. The generic In \ref{ch:nonclass}: \code{realpart()} and \code{imagpart()} of a \code{SparseMat<complex<double>>} or \code{SparseMat<complex<float>>}. The generic
matrix functions in \code{matexp.h} (e.g.\ \code{exp()}) work with \code{SparseMat}, and the iterative solvers matrix functions in \code{matexp.h} (e.g.\ \code{exp()}) work with \code{SparseMat}, and the iterative solvers
of \ref{ch:iterative} use its \code{gemv}, \code{diagonalof}, \code{issymmetric}, \code{nrows} interface. of \ref{ch:iterative} use its \code{gemv}, \code{diagonalof}, \code{issymmetric}, \code{nrows} interface.
@@ -245,10 +248,10 @@ copies).
\lafunc{SparseSMat()}{Undefined empty matrix.} \lafunc{SparseSMat()}{Undefined empty matrix.}
\lafunc{explicit SparseSMat(const SPMatindex n); explicit SparseSMat(const SPMatindex n, const SPMatindex m)}{Empty (zero) $n\times n$ or $n\times m$ matrix.} \lafunc{explicit SparseSMat(const SPMatindex n); explicit SparseSMat(const SPMatindex n, const SPMatindex m)}{Empty (zero) $n\times n$ or $n\times m$ matrix.}
\lafunc{SparseSMat(const SparseSMat &rhs)}{Shallow copy.} \lafunc{SparseSMat(const SparseSMat &rhs)}{Shallow copy.}
\lafunc{explicit SparseSMat(const NRSMat<T> &rhs)}{From a packed symmetric (complex: hermitian) matrix (both triangles inserted, the upper one conjugated, $|a_{ij}|>$ \code{SPARSEEPSILON}).} \lafunc{explicit SparseSMat(const NRSMat<T> &rhs)}{From a packed symmetric (complex: hermitian) matrix (both triangles inserted, the upper one conjugated, $|a_{ij}|>$ $\epsilon_{\rm sp}$).}
\lafunc{explicit SparseSMat(const NRMat<T> &rhs)}{From a dense $n\times m$ matrix, nonzero elements copied as they are (no symmetrization).} \lafunc{explicit SparseSMat(const NRMat<T> &rhs)}{From a dense $n\times m$ matrix, nonzero elements copied as they are (no symmetrization).}
\lafunc{explicit SparseSMat(const SparseMat<T> &rhs)}{From a \code{SparseMat} (same dimensions); duplicates are summed, a symmetric \code{SparseMat} is unfolded without conjugation (complex symmetric, as \code{SparseMat} defines it).} \lafunc{explicit SparseSMat(const SparseMat<T> &rhs)}{From a \code{SparseMat} (same dimensions); duplicates are summed, a symmetric \code{SparseMat} is unfolded without conjugation (complex symmetric, as \code{SparseMat} defines it).}
\lafunc{explicit SparseSMat(const CSRMat<T> &rhs)}{Declared but not implemented (\code{CSRMat} is not supported).} \lafunc{explicit SparseSMat(const CSRMat<T> &rhs)}{From a \code{CSRMat} (section~\ref{sec:sparse:csr}): all stored elements, without mirroring.}
\lafunc{~SparseSMat()}{Destructor.} \lafunc{~SparseSMat()}{Destructor.}
\lafunc{SparseSMat & operator=(const SparseSMat &rhs)}{Shallow copy.} \lafunc{SparseSMat & operator=(const SparseSMat &rhs)}{Shallow copy.}
\lafunc{SparseSMat & operator=(const T &a)}{Scalar matrix $a\cdot1$ (diagonal elements are created even for $a=0$).} \lafunc{SparseSMat & operator=(const T &a)}{Scalar matrix $a\cdot1$ (diagonal elements are created even for $a=0$).}
@@ -266,7 +269,7 @@ Dense from sparse: \code{explicit NRMat<T>(const SparseSMat<T>&)} copies all sto
\lafunc{inline void add(const SPMatindex n, const SPMatindex m, const T elem, const bool both=true)}{Add \code{elem} to element $(n,m)$ and, if \code{both} and $n\ne m$, its complex conjugate to $(m,n)$. Creates the row map if needed; no \code{copyonwrite()}; range check in DEBUG only. Resulting zeros remain until \code{simplify()}.} \lafunc{inline void add(const SPMatindex n, const SPMatindex m, const T elem, const bool both=true)}{Add \code{elem} to element $(n,m)$ and, if \code{both} and $n\ne m$, its complex conjugate to $(m,n)$. Creates the row map if needed; no \code{copyonwrite()}; range check in DEBUG only. Resulting zeros remain until \code{simplify()}.}
\lafunc{inline const std::map<SPMatindex,T> *line(const SPMatindex n) const}{Read-only access to row $n$ (\code{NULL} if empty).} \lafunc{inline const std::map<SPMatindex,T> *line(const SPMatindex n) const}{Read-only access to row $n$ (\code{NULL} if empty).}
\lafunc{inline ROWTYPE & operator[](const SPMatindex i)}{Row $i$ as a reference to the pointer \code{std::map<SPMatindex,T>*} (\code{typedef ROWTYPE}), possibly \code{NULL}. Non-const only.} \lafunc{inline ROWTYPE & operator[](const SPMatindex i)}{Row $i$ as a reference to the pointer \code{std::map<SPMatindex,T>*} (\code{typedef ROWTYPE}), possibly \code{NULL}. Non-const only.}
\lafunc{unsigned long long simplify()}{Remove elements with $|a_{ij}|<$ \code{SPARSEEPSILON} and empty rows; returns the number of remaining elements.} \lafunc{unsigned long long simplify()}{Remove elements with $|a_{ij}|<$ $\epsilon_{\rm sp}$ and empty rows; returns the number of remaining elements.}
\lafunc{inline unsigned long long length()}{\code{simplify()} (non-const).} \lafunc{inline unsigned long long length()}{\code{simplify()} (non-const).}
\lafunc{int nrows() const; int ncols() const; inline bool issymmetric() const}{Dimensions; \code{issymmetric()} is always true.} \lafunc{int nrows() const; int ncols() const; inline bool issymmetric() const}{Dimensions; \code{issymmetric()} is always true.}
\lafunc{iterator begin() const; iterator end() const}{Iterator over all stored elements, row by row in increasing column order. \code{*it} is a \code{matel<T>} \emph{copy} held in the iterator (writing to it does not change the matrix). It is meant for comparison with \code{end()} or via \code{it.notend()}; \code{iterator(const SparseSMat&)} starts at the first element.} \lafunc{iterator begin() const; iterator end() const}{Iterator over all stored elements, row by row in increasing column order. \code{*it} is a \code{matel<T>} \emph{copy} held in the iterator (writing to it does not change the matrix). It is meant for comparison with \code{end()} or via \code{it.notend()}; \code{iterator(const SparseSMat&)} starts at the first element.}
@@ -288,7 +291,7 @@ Dense from sparse: \code{explicit NRMat<T>(const SparseSMat<T>&)} copies all sto
\lafunc{const T* diagonalof(NRVec<T> &r, const bool divide=0, bool cache=false) const}{As for \code{SparseMat} (square matrices only).} \lafunc{const T* diagonalof(NRVec<T> &r, const bool divide=0, bool cache=false) const}{As for \code{SparseMat} (square matrices only).}
\lafunc{SparseSMat transpose(bool conj=false) const}{Transposed (optionally conjugated) copy.} \lafunc{SparseSMat transpose(bool conj=false) const}{Transposed (optionally conjugated) copy.}
\lafunc{void transposeme() const}{Raises an error (not implemented).} \lafunc{void transposeme() const}{Raises an error (not implemented).}
\lafunc{SparseSMat<T> cholesky(void) const}{Pivoted Cholesky decomposition of a positive semidefinite symmetric/hermitian matrix (not checked): returns $R$ with $R\Hc R = A$ (\code{result.transpose(true)*result} reproduces the input). The pivoting is done a priori by sorting the diagonal; pivots with real part below \code{CHOLESKYEPSILON}$=10^{-16}$ are skipped (rank-deficient case), elements below \code{SPARSEEPSILON} are dropped. $R$ is upper triangular only in the permuted basis.} \lafunc{SparseSMat<T> cholesky(void) const}{Pivoted Cholesky decomposition of a positive semidefinite symmetric/hermitian matrix (not checked): returns $R$ with $R\Hc R = A$ (\code{result.transpose(true)*result} reproduces the input). The pivoting is done a priori by sorting the diagonal; pivots with real part below \code{LA_precision<normtype>::cholesky_epsilon()} ($10^{-16}$ for double, the old \code{CHOLESKYEPSILON}, $10^{-8}$ for float) are skipped (rank-deficient case), elements below $\epsilon_{\rm sp}$ are dropped. $R$ is upper triangular only in the permuted basis.}
\lafunc{SparseSMat submatrix(const int fromrow, const int torow, const int fromcol, const int tocol) const}{Block (inclusive bounds).} \lafunc{SparseSMat submatrix(const int fromrow, const int torow, const int fromcol, const int tocol) const}{Block (inclusive bounds).}
\lafunc{void storesubmatrix(const int fromrow, const int fromcol, const SparseSMat &rhs)}{Store \code{rhs} into the block starting at $(\mathtt{fromrow},\mathtt{fromcol})$: the old elements of the block are removed first (as \code{NRMat::storesubmatrix}); calls \code{copyonwrite()}.} \lafunc{void storesubmatrix(const int fromrow, const int fromcol, const SparseSMat &rhs)}{Store \code{rhs} into the block starting at $(\mathtt{fromrow},\mathtt{fromcol})$: the old elements of the block are removed first (as \code{NRMat::storesubmatrix}); calls \code{copyonwrite()}.}
\end{lareference} \end{lareference}
@@ -302,19 +305,108 @@ Dense from sparse: \code{explicit NRMat<T>(const SparseSMat<T>&)} copies all sto
\lafunc{SparseSMat<T> otimes_sparse(const NRVec<T> &lhs, const NRVec<T> &rhs, const bool conjugate=false, const T &scale=1)}{\emph{Symmetrized} outer product, symmetric (complex: hermitian) regardless of the input vectors (unlike \code{SparseMat}): \lafunc{SparseSMat<T> otimes_sparse(const NRVec<T> &lhs, const NRVec<T> &rhs, const bool conjugate=false, const T &scale=1)}{\emph{Symmetrized} outer product, symmetric (complex: hermitian) regardless of the input vectors (unlike \code{SparseMat}):
with $M=\mathtt{scale}\cdot l\,r\Tr$ (or $l\,r\Hc$ if \code{conjugate}) the result is $S=M+M\Hc$ ($=l\,r\Tr+r\,l\Tr$ for real types), with $M=\mathtt{scale}\cdot l\,r\Tr$ (or $l\,r\Hc$ if \code{conjugate}) the result is $S=M+M\Hc$ ($=l\,r\Tr+r\,l\Tr$ for real types),
including the doubled diagonal $S_{ii}=M_{ii}+M_{ii}^*$. Note that \code{otimes_sparse(x,x,true)} is $2\,x x\Hc$; use \code{scale=0.5} for $x x\Hc$. including the doubled diagonal $S_{ii}=M_{ii}+M_{ii}^*$. Note that \code{otimes_sparse(x,x,true)} is $2\,x x\Hc$; use \code{scale=0.5} for $x x\Hc$.
Elements with $|S_{ij}|\le$ \code{SPARSEEPSILON} are omitted. Both vectors must have the same length (\code{laerror} otherwise).} Elements with $|S_{ij}|\le$ $\epsilon_{\rm sp}$ are omitted. Both vectors must have the same length (\code{laerror} otherwise).}
\lafunc{SparseSMat<T> NRMat<T>::operator*(const SparseSMat<T> &rhs) const}{Declared in mat.h: dense times sparse, general product $AS$ with a sparse result (row $i$ accumulated as $\sum_k A_{ik}\,(\text{row }k\text{ of }S)$, zero $A_{ik}$ skipped; simplified). Instantiated for \code{double} and \code{std::complex<double>}.} \lafunc{SparseSMat<T> NRMat<T>::operator*(const SparseSMat<T> &rhs) const}{Declared in mat.h: dense times sparse, general product $AS$ with a sparse result (row $i$ accumulated as $\sum_k A_{ik}\,(\text{row }k\text{ of }S)$, zero $A_{ik}$ skipped; simplified). Instantiated for \code{double} and \code{std::complex<double>}.}
\end{lareference} \end{lareference}
In \ref{ch:nonclass}: \code{complexmatrix()}, \code{realmatrix()} and \code{imagmatrix()} build a In \ref{ch:nonclass}: \code{complexmatrix()}, \code{realmatrix()} and \code{imagmatrix()} build a
\code{SparseSMat<complex<double>>} from real ones. The generic \code{exp()} of \code{matexp.h} works with \code{SparseSMat<complex<double>>} (\code{SparseSMat<complex<float>>}) from real ones. The generic \code{exp()} of \code{matexp.h} works with
\code{SparseSMat}. \code{SparseSMat}.
\section{CSRMat}\label{sec:sparse:csr} \section{CSRMat}\label{sec:sparse:csr}
\laheader{csrmat.h} \laheader{csrmat.h, csrmat.cc}
\code{CSRMat<T>} is meant to hold a matrix in compressed sparse row format (row pointers, column indices, \code{CSRMat<T>} stores a matrix in the compressed sparse row format: the elements of row $i$ are \code{v[k]} in
values, optionally on the GPU via cuSPARSE) for fast products. The class is unfinished: only the declarations column \code{col[k]} for $k=$ \code{rowptr[i]}$,\ldots,$\code{rowptr[i+1]}$-1$, with the columns strictly ascending within
exist, \code{csrmat.cc} contains no implementation and no instantiation. It is \emph{not supported} and must not be used; each row (an invariant of the class). The format is read-efficient: $Ax$ is a row-wise dot product without any
the declarations (also \code{NRMat(const CSRMat&)} and \code{SparseSMat(const CSRMat&)}) are kept for possible future work. indirection in the result, which makes it the fastest of the sparse classes in iterative solvers (\ref{ch:iterative})
and matrix functions with a fixed sparsity pattern. It cannot be built element by element; a matrix is assembled as a
\code{SparseMat} (\code{add}) or a \code{SparseSMat} (or as three arrays) and converted, in $O(n_{\rm nz}+n+m)$.
The full matrix is always stored, also for symmetric and hermitian matrices. The flag \code{symmetric} only
\emph{declares} the matrix hermitian, $A=A^\dagger$ (real symmetric for real types); it is not checked and never changes the
arithmetic, but it is what \code{issymmetric()} returns to the eigensolvers (\code{davidson} uses the hermitian algorithm,
\code{lanczos} requires it). This is the convention of \code{NRSMat} and \code{SparseSMat}, not the complex symmetric
one of \code{SparseMat}.
The three arrays are \code{NRVec} members (\code{rowptr} of length $n+1$, \code{col} and \code{v} of length $n_{\rm nz}$, the
number of stored elements); each of them shares its data independently (section~\ref{sec:concepts:refcount}). Operations which
change only the values keep the index arrays shared, e.g.\ \code{B=A*2.}, \code{A+=a} with all diagonal elements stored, or
\code{realmatrix(A)}; \code{copyonwrite()} copies all three. Conversions store only elements with $|a_{ij}|>\epsilon_{\rm sp}$,
whereas arithmetic keeps elements which cancel to zero, until \code{simplify()} is called. $n_{\rm nz}$ is limited to $2^{31}-1$
(the \code{NRVec} length). The class is instantiated for \code{double}, \code{float}, \code{std::complex<double>} and
\code{std::complex<float>}; all operations are CPU only (\code{laerror} for vectors and matrices on the GPU), the device-dependent
format is the one of cuSPARSE, so a GPU version can be added later.
\subsection{Construction, conversion, access}
\begin{lareference}
\lafunc{CSRMat()}{Empty $0\times0$ matrix.}
\lafunc{explicit CSRMat(const SPMatindex n, const SPMatindex m); explicit CSRMat(const SPMatindex n)}{Zero $n\times m$ resp.\ $n\times n$ matrix.}
\lafunc{CSRMat(const SPMatindex n, const SPMatindex m, const NRVec<SPMatindex> &rowptr, const NRVec<SPMatindex> &col, const NRVec<T> &v)}{From the three arrays, which are shared (not copied). In DEBUG their consistency, the column range and the ascending columns are checked.}
\lafunc{explicit CSRMat(const SparseMat<T> &rhs)}{Duplicates are summed, elements with $|a_{ij}|\le\epsilon_{\rm sp}$ dropped. A symmetric \code{SparseMat} is unfolded without conjugation (complex symmetric, as \code{SparseMat} defines it); the flag is set for the real types only.}
\lafunc{explicit CSRMat(const SparseSMat<T> &rhs); explicit CSRMat(const NRMat<T> &rhs)}{Elements with $|a_{ij}|>\epsilon_{\rm sp}$; the flag is not set (call \code{setsymmetric()} if appropriate).}
\lafunc{explicit CSRMat(const NRSMat<T> &rhs)}{Hermitian as \code{NRSMat}: the upper triangle is the conjugate of the stored lower one; the flag is set.}
\lafunc{void copyonwrite(bool detachonly=false, bool deep=true); void resize(const SPMatindex n, const SPMatindex m); void dealloc(); void clear(); void swap(CSRMat &rhs)}{As for the other classes; \code{resize} gives a zero matrix and clears the flag, \code{clear} keeps it.}
\lafunc{SPMatindex nrows() const; SPMatindex ncols() const; int length() const}{Dimensions and $n_{\rm nz}$.}
\lafunc{bool issymmetric() const; void setsymmetric(); void setunsymmetric()}{The hermitian flag; \code{setsymmetric} only declares (square matrices only).}
\lafunc{const NRVec<SPMatindex> & getrowptr() const; const NRVec<SPMatindex> & getcol() const; const NRVec<T> & getval() const}{Read access to the arrays.}
\lafunc{const T operator()(const SPMatindex i, const SPMatindex j) const}{Element $a_{ij}$ by binary search in row $i$, zero if not stored. There is no write access to single elements.}
\lafunc{iterator begin() const; iterator end() const}{Read-only iterator over the stored elements in row-major order: \code{p->row}, \code{p->col}, \code{p->elem} (a \code{matel<T>}), \code{p.notend()}.}
\end{lareference}
Other classes from \code{CSRMat}: \code{explicit NRMat<T>(const CSRMat<T>&)} (full matrix), \code{explicit NRSMat<T>(const CSRMat<T>&)}
(lower triangle, square only, as from \code{SparseSMat}), \code{explicit SparseMat<T>(const CSRMat<T>&)} (not symmetric),
\code{explicit SparseSMat<T>(const CSRMat<T>&)} and \code{explicit NRVec<T>(const CSRMat<T>&)} (for a $1\times m$ or $n\times 1$ matrix).
\subsection{Arithmetic}
\begin{lareference}
\lafunc{CSRMat & operator*=(const T &a); const CSRMat operator*(const T &a) const}{Scaling of the values (BLAS); the index arrays stay shared, $a=0$ gives the zero matrix.}
\lafunc{CSRMat & operator=(const T &a); void identity()}{$a\cdot1$ resp.\ the unit matrix (square only).}
\lafunc{CSRMat & operator+=(const T &a); CSRMat & operator-=(const T &a); const CSRMat operator+(const T &a) const; const CSRMat operator-(const T &a) const}{$A\pm a\cdot1$ (square only): in place if all diagonal elements are stored, otherwise by a merge.}
\lafunc{void axpy(const T alpha, const CSRMat &x, const bool transp=false)}{$A\leftarrow A+\alpha x$ (or $\alpha x\Tr$): merge of the sorted rows in two passes; for the same structure (shared or equal index arrays) a BLAS \code{axpy} of the values. Elements which cancel stay stored.}
\lafunc{CSRMat & operator+=(const CSRMat &rhs); CSRMat & operator-=(const CSRMat &rhs); const CSRMat operator+(const CSRMat &rhs) const; const CSRMat operator-(const CSRMat &rhs) const}{Via \code{axpy}.}
\lafunc{const typename LA_traits<T>::normtype norm(const T scalar=(T)0) const}{Frobenius norm of $A-\mathrm{scalar}\cdot1$.}
\lafunc{const T dot(const CSRMat &rhs) const; const T dot(const NRMat<T> &rhs) const}{$\sum_{ij}\bar a_{ij}b_{ij}$, conjugating the left operand as \code{NRMat::dot} (unlike \code{SparseMat::dot}).}
\lafunc{const T trace() const}{Sum of the diagonal elements.}
\lafunc{void simplify(const typename LA_traits<T>::normtype sparseepsilon=LA_precision<normtype>::sparse_epsilon())}{Removes the elements with $|a_{ij}|\le$ \code{sparseepsilon}.}
\end{lareference}
The result of an addition is declared hermitian if both operands are (a matrix without stored elements counts as hermitian) and the
scalar factor is real; multiplying by or adding a non-real scalar clears the flag. In other classes:
\code{NRMat<T>::operator+=(const CSRMat<T>&)} and \code{operator-=}.
\subsection{Products}
\begin{lareference}
\lafunc{void gemv(const T beta, NRVec<T> &r, const char trans, const T alpha, const NRVec<T> &x) const}{$r\leftarrow\beta r+\alpha\,\mathrm{op}(A)x$, \code{trans} \code{'n'}, \code{'t'} or \code{'c'} (conjugate transposition, the same as \code{'t'} for real types); forwards to \code{NRVec<T>::gemv}. The size of \code{x} and (in DEBUG) of the result are checked, $\beta=0$ clears the result.}
\lafunc{inline const NRVec<T> operator*(const NRVec<T> &rhs) const}{$Ax$.}
\lafunc{const T* diagonalof(NRVec<T> &r, const bool divide=0, bool cache=false) const}{Diagonal (binary search in each row), or $r$ divided by it elementwise (zero diagonal elements skipped). For a non-square matrix the diagonal of $A\Tr A$ ($\sum_i a_{ij}^2$, no conjugation, as \code{SparseMat}), used as the preconditioner of \code{conjgrad}. \code{cache} is ignored.}
\lafunc{inline const NRMat<T> operator*(const NRMat<T> &rhs) const}{$AB$ with a dense result, via \code{NRMat<T>::gemm} below.}
\lafunc{void gemm(const T beta, const CSRMat &a, const char transa, const CSRMat &b, const char transb, const T alpha)}{$C\leftarrow\beta C+\alpha\,\mathrm{op}(A)\,\mathrm{op}(B)$ with a sparse result (\code{trans} as for \code{gemv}), by the row-wise algorithm of Gustavson with a dense accumulator \cite{sparse:gustavson1978} in two passes; the rows of the result are sorted, elements which cancel stay stored, the result is not declared hermitian. With $\beta=0$ the dimensions of \code{*this} do not matter.}
\lafunc{inline const CSRMat operator*(const CSRMat &rhs) const}{$AB$.}
\end{lareference}
In other classes: \code{NRVec<T>::gemv(const T beta, const CSRMat<T> &a, const char trans, const T alpha, const NRVec &x)},
\code{NRVec<T>::operator*(const CSRMat<T>&)} ($x\Tr A$),
\code{NRMat<T>::gemm(const T &beta, const CSRMat<T> &a, const char transa, const NRMat &b, const char transb, const T &alpha)}
(all combinations of \code{'n'}, \code{'t'}, \code{'c'}) and \code{NRMat<T>::operator*(const CSRMat<T>&)} (dense times sparse, dense result).
The sparse kernels are plain loops (there is no sparse BLAS in the libraries used).
\subsection{Transposition, permutations, direct sums and products}
\begin{lareference}
\lafunc{const CSRMat transpose(bool conj=false) const; void transposeme(bool conj=false)}{$A\Tr$ or $A\Hc$ by a counting sort, $O(n_{\rm nz}+n+m)$; the flag is kept.}
\lafunc{void permuteme_rows(const NRPerm<int> &p, const bool inverse=false); void permuteme_cols(const NRPerm<int> &p, const bool inverse=false); void permuteme_both(const NRPerm<int> &p, const NRPerm<int> &q, const bool inverse=false)}{The convention of \code{SparseMat}: element $(i,j)$ moves to row $p_{i+1}-1$ resp.\ column $q_{j+1}-1$ (matrix indices from 0, permutations from 1, optionally inverted). Since the full matrix is stored, \code{permuteme_rows} permutes only the rows also of a hermitian matrix (unlike a symmetric \code{SparseMat}); the flag is cleared, except by \code{permuteme_both} with $p=q$.}
\lafunc{const CSRMat permuted_rows(...) const; const CSRMat permuted_cols(...) const; const CSRMat permuted_both(...) const}{Out-of-place versions.}
\lafunc{void permuterows(const NRVec<SPMatindex> &p); void permutecolumns(const NRVec<SPMatindex> &p); void permuteindices(const NRVec<SPMatindex> &p)}{As for \code{SparseMat}: $i\to p_i$ (indices from 0), \code{permuteindices} for rows and columns.}
\lafunc{CSRMat & oplusequal(const CSRMat &rhs); const CSRMat oplus(const CSRMat &rhs) const}{Direct sum $A\oplus B$; also with \code{NRMat} and \code{NRSMat} (converted as above, i.e.\ \code{NRSMat} as hermitian).}
\lafunc{const CSRMat otimes(const CSRMat &rhs) const}{Direct (Kronecker) product, $(A\otimes B)_{i n_B+k,\,j m_B+l}=a_{ij}b_{kl}$ as \code{NRMat::operator|}; also with \code{NRMat} and \code{NRSMat}.}
\end{lareference}
The results of \code{oplus} and \code{otimes} are declared hermitian if both operands are.
\subsection{Input/output and free functions}
\begin{lareference}
\lafunc{void put(int fd, bool dimensions=1, bool transp=0, bool orcaformat=false) const; void get(int fd, bool dimensions=1, bool transp=0, bool orcaformat=false)}{Binary I/O: if \code{dimensions}, $n$ and $m$ (\code{SPMatindex}); the flag (\code{int}); the three arrays by \code{NRVec::put} with their lengths. \code{transp} writes or reads the transposed matrix; \code{orcaformat=true} raises \code{laerror}.}
\lafunc{std::ostream& operator<<(std::ostream &s, const CSRMat<T> &x); std::istream& operator>>(std::istream &s, CSRMat<T> &x)}{Text I/O in the format of \code{SparseMat} (read through a \code{SparseMat}, so duplicates are summed); the flag is not written.}
\lafunc{inline const CSRMat<T> commutator(const CSRMat<T> &x, const CSRMat<T> &y, const bool trx=0, const bool tryy=0); inline const CSRMat<T> anticommutator(...)}{$xy\mp yx$ via two \code{gemm} calls.}
\end{lareference}
In \ref{ch:nonclass}: \code{realpart()}, \code{imagpart()} of a complex \code{CSRMat} and \code{realmatrix()}, \code{imagmatrix()}, \code{complexmatrix()}
of real ones; only the values are converted (BLAS), the index arrays stay shared unless elements which became zero are removed.
The real part of a hermitian matrix and \code{realmatrix} of a declared symmetric one keep the flag. The templates of \code{matexp.h}
(\code{exp()}, \code{sincos()}, \code{exptimes()}) and the iterative solvers of \ref{ch:iterative} work with \code{CSRMat}.
\section{Examples}\label{sec:sparse:examples} \section{Examples}\label{sec:sparse:examples}
\begin{lstlisting} \begin{lstlisting}
@@ -353,6 +445,15 @@ h.add(2,2,1.);
SparseSMat<double> h2 = h*h; //result stays symmetric SparseSMat<double> h2 = h*h; //result stays symmetric
NRVec<double> w = h*x; NRVec<double> w = h*x;
std::cout << y << z << w << NRMat<double>(h2) << sd; std::cout << y << z << w << NRMat<double>(h2) << sd;
//compressed row format: assembled as SparseMat, then converted
SparseMat<double> hb(4,4);
hb.add(0,0,1.); hb.add(1,0,0.2); hb.add(0,1,0.2); hb.add(3,3,2.);
CSRMat<double> hc(hb);
hc.setsymmetric(); //declare it symmetric, e.g. for davidson
NRVec<double> u = hc*x;
CSRMat<std::complex<double> > ev = exp(imagmatrix(hc)); //exp(iH), sparse
CSRMat<double> hc2 = hc*2.; //shares rowptr and col with hc
} }
\end{lstlisting} \end{lstlisting}
@@ -364,4 +465,7 @@ std::cout << y << z << w << NRMat<double>(h2) << sd;
\item In a symmetric \code{SparseMat} never store both $a_{ij}$ and $a_{ji}$; \code{setsymmetric()} does not check this. \item In a symmetric \code{SparseMat} never store both $a_{ij}$ and $a_{ji}$; \code{setsymmetric()} does not check this.
\item \code{SparseSMat::gemm} computes $A\Tr B$ (or $A\Hc B$); use \code{operator*} for the general product. \item \code{SparseSMat::gemm} computes $A\Tr B$ (or $A\Hc B$); use \code{operator*} for the general product.
\item Complex \code{SparseMat} matrices are complex symmetric, never hermitian, in all mirroring operations, whereas \code{SparseSMat} (\code{add} with \code{both=true}, conversion from \code{NRSMat}, \code{otimes_sparse}) is hermitian. \item Complex \code{SparseMat} matrices are complex symmetric, never hermitian, in all mirroring operations, whereas \code{SparseSMat} (\code{add} with \code{both=true}, conversion from \code{NRSMat}, \code{otimes_sparse}) is hermitian.
\item \code{CSRMat} cannot be filled element by element; assemble a \code{SparseMat} or \code{SparseSMat} and convert it.
\item The flag of \code{CSRMat} only declares hermiticity. Conversions from \code{NRMat}, \code{SparseSMat} and arrays (and from a complex
symmetric \code{SparseMat}) do not set it; call \code{setsymmetric()} for a hermitian matrix before \code{davidson} or \code{lanczos}.
\end{itemize} \end{itemize}
+3 -3
View File
@@ -18,8 +18,8 @@ Typical uses are many-body quantities (amplitudes, integrals, density matrices)
in groups of indices should be exploited for storage. in groups of indices should be exploited for storage.
Limitations stated in the source: Limitations stated in the source:
\begin{itemize} \begin{itemize}
\item the implementation is ``rudimentary''; only \code{Tensor<double>} and \code{Tensor<std::complex<double> >} \item the implementation is ``rudimentary''; only \code{Tensor<double>}, \code{Tensor<std::complex<double> >} and their single precision
are instantiated (tensor.cc); counterparts \code{Tensor<float>}, \code{Tensor<std::complex<float> >} are instantiated (tensor.cc);
\item index types \code{LA_index} and \code{LA_largeindex} are \code{int}, so the total size is limited \item index types \code{LA_index} and \code{LA_largeindex} are \code{int}, so the total size is limited
to $2^{31}$ elements (\code{NRVec} limitation); to $2^{31}$ elements (\code{NRVec} limitation);
\item symmetry \emph{between} index groups (e.g.\ $(ij|kl)=(kl|ij)$) is not supported; one would have to \item symmetry \emph{between} index groups (e.g.\ $(ij|kl)=(kl|ij)$) is not supported; one would have to
@@ -291,7 +291,7 @@ Names are permuted together with the indices unless stated otherwise.
\begin{lareference} \begin{lareference}
\lafunc{void apply_permutation_algebra(const Tensor &rhs, const PermutationAlgebra<int,T> &pa, bool inverse=false, T alpha=1, T beta=0, bool conjugate_by_parity=false)}{$\mathtt{*this} \leftarrow \beta\,\mathtt{*this}$, then for all stored elements $I$ of \code{*this}: $\mathtt{this}(I) \mathrel{+}= \alpha \sum_P c_P\, \mathtt{rhs}(P(I))$, with $P$ acting on the flat index list (inverse permutations if \code{inverse}); with \code{conjugate_by_parity} odd permutations take the complex conjugate. Can represent general (anti)symmetrizers, e.g.\ \code{general_antisymmetrizer} (chapter~\ref{ch:permutation}). \code{*this} may have a symmetry which \code{rhs} lacks. The names of \code{*this} are taken from (or checked against) the names of \code{rhs} permuted by the first permutation. Element-by-element, not optimally efficient.} \lafunc{void apply_permutation_algebra(const Tensor &rhs, const PermutationAlgebra<int,T> &pa, bool inverse=false, T alpha=1, T beta=0, bool conjugate_by_parity=false)}{$\mathtt{*this} \leftarrow \beta\,\mathtt{*this}$, then for all stored elements $I$ of \code{*this}: $\mathtt{this}(I) \mathrel{+}= \alpha \sum_P c_P\, \mathtt{rhs}(P(I))$, with $P$ acting on the flat index list (inverse permutations if \code{inverse}); with \code{conjugate_by_parity} odd permutations take the complex conjugate. Can represent general (anti)symmetrizers, e.g.\ \code{general_antisymmetrizer} (chapter~\ref{ch:permutation}). \code{*this} may have a symmetry which \code{rhs} lacks. The names of \code{*this} are taken from (or checked against) the names of \code{rhs} permuted by the first permutation. Element-by-element, not optimally efficient.}
\lafunc{void apply_permutation_algebra(const NRVec<Tensor> &rhsvec, const PermutationAlgebra<int,T> &pa, bool inverse=false, T alpha=1, T beta=0)}{the same with \code{rhs} being the outer product of the tensors in \code{rhsvec}, which is not formed explicitly (e.g.\ Grassmann products building a density matrix from cumulants); rather inefficient.} \lafunc{void apply_permutation_algebra(const NRVec<Tensor> &rhsvec, const PermutationAlgebra<int,T> &pa, bool inverse=false, T alpha=1, T beta=0)}{the same with \code{rhs} being the outer product of the tensors in \code{rhsvec}, which is not formed explicitly (e.g.\ Grassmann products building a density matrix from cumulants); rather inefficient.}
\lafunc{NRVec<NRMat<T> > Tucker(typename LA_traits<T>::normtype thr=1e-12, bool inverseorder=false)}{HOSVD/Tucker decomposition: \code{*this} is replaced by the flat core tensor and the factor matrices (one per index, columns orthonormal) are returned; singular values below \code{thr} are dropped, which reduces the corresponding range of the core.} \lafunc{NRVec<NRMat<T> > Tucker(typename LA_traits<T>::normtype thr=LA_precision<normtype>::svd_threshold(), bool inverseorder=false)}{HOSVD/Tucker decomposition: \code{*this} is replaced by the flat core tensor and the factor matrices (one per index, columns orthonormal) are returned; singular values below \code{thr} are dropped, which reduces the corresponding range of the core. The tensor is $X=G\times_1U_1\cdots\times_rU_r$ with the core $G$ and the factor matrices $U_k$, contracted without complex conjugation ($G=X\times_1U_1^\dagger\cdots\times_rU_r^\dagger$).}
\lafunc{Tensor inverseTucker(const NRVec<NRMat<T> > &x, bool inverseorder=false) const}{rebuild the tensor from the core (\code{*this}) and the matrices returned by \code{Tucker} with the same \code{inverseorder}.} \lafunc{Tensor inverseTucker(const NRVec<NRMat<T> > &x, bool inverseorder=false) const}{rebuild the tensor from the core (\code{*this}) and the matrices returned by \code{Tucker} with the same \code{inverseorder}.}
\lafunc{Tensor linear_transform(const NRVec<NRMat<T> > &x) const}{transform every index of group $g$ by the matrix \code{x[g]} (\code{x[g].ncols()} must equal the range; the new range is \code{x[g].nrows()}), preserving the group symmetries: $t'_{\dots a \dots} = \sum_i x_{ai}\, t_{\dots i\dots}$.} \lafunc{Tensor linear_transform(const NRVec<NRMat<T> > &x) const}{transform every index of group $g$ by the matrix \code{x[g]} (\code{x[g].ncols()} must equal the range; the new range is \code{x[g].nrows()}), preserving the group symmetries: $t'_{\dots a \dots} = \sum_i x_{ai}\, t_{\dots i\dots}$.}
\lafunc{Tensor linear_transform(const int g, const Tensor<T> &x) const; Tensor linear_transform(const int g, const NRMat<T> &x) const}{transform only group \code{g}, keeping its position and symmetry; \code{x} must be a flat rank-2 tensor (useful for raising/lowering indices) or a matrix.} \lafunc{Tensor linear_transform(const int g, const Tensor<T> &x) const; Tensor linear_transform(const int g, const NRMat<T> &x) const}{transform only group \code{g}, keeping its position and symmetry; \code{x} must be a flat rank-2 tensor (useful for raising/lowering indices) or a matrix.}
+3 -1
View File
@@ -66,6 +66,7 @@ one: \code{rhs} becomes the real part (or the imaginary part if \code{imagpart})
$n(n+1)/2$ elements. \emph{Shares} the data with \code{S} (no copy).} $n(n+1)/2$ elements. \emph{Shares} the data with \code{S} (no copy).}
\lafunc{explicit NRVec(const NRMat<T> &rhs)}{View of the row-major storage of an $n\times m$ matrix as a vector of $nm$ elements. \lafunc{explicit NRVec(const NRMat<T> &rhs)}{View of the row-major storage of an $n\times m$ matrix as a vector of $nm$ elements.
\emph{Shares} the data with \code{rhs}; if the library is compiled with \code{MATPTR}, the data are copied instead.} \emph{Shares} the data with \code{rhs}; if the library is compiled with \code{MATPTR}, the data are copied instead.}
\lafunc{explicit NRVec(const CSRMat<T> &rhs)}{The same from a compressed row sparse matrix (section~\ref{sec:sparse:csr}).}
\lafunc{explicit NRVec(const SparseMat<T> &rhs)}{Dense vector from a sparse matrix having one row or one column (otherwise \lafunc{explicit NRVec(const SparseMat<T> &rhs)}{Dense vector from a sparse matrix having one row or one column (otherwise
\code{laerror}); duplicate entries are summed. Defined in \texttt{sparsemat.cc} for \code{double} and \code{std::complex<double>}.} \code{laerror}); duplicate entries are summed. Defined in \texttt{sparsemat.cc} for \code{double} and \code{std::complex<double>}.}
\lafunc{~NRVec()}{Decrements the reference count, frees the data when it reaches zero.} \lafunc{~NRVec()}{Decrements the reference count, frees the data when it reaches zero.}
@@ -162,6 +163,7 @@ with $y=$ \code{*this}; \code{trans} is \code{'n'} ($A$), \code{'t'} ($A\Tr$) or
\code{*this} must already have the correct size (checked in DEBUG builds) and defined contents if $\beta\neq0$.} \code{*this} must already have the correct size (checked in DEBUG builds) and defined contents if $\beta\neq0$.}
\lafunc{void gemv(const T beta, const NRSMat<T> &a, const char trans, const T alpha, const NRVec &x)}{Same for a packed \lafunc{void gemv(const T beta, const NRSMat<T> &a, const char trans, const T alpha, const NRVec &x)}{Same for a packed
symmetric (real) or Hermitian (complex) matrix (BLAS spmv/hpmv); \code{trans}: for complex \code{'t'} gives $a\Tr x = a^* x$, \code{'n'} and \code{'c'} give $a x$; ignored for real matrices.} symmetric (real) or Hermitian (complex) matrix (BLAS spmv/hpmv); \code{trans}: for complex \code{'t'} gives $a\Tr x = a^* x$, \code{'n'} and \code{'c'} give $a x$; ignored for real matrices.}
\lafunc{void gemv(const T beta, const CSRMat<T> &a, const char trans, const T alpha, const NRVec &x)}{The same for a compressed row sparse matrix, \code{trans} \code{'n'}, \code{'t'} or \code{'c'} (section~\ref{sec:sparse:csr}).}
\lafunc{void gemv(const T beta, const SparseMat<T> &a, const char trans, const T alpha, const NRVec &x, const bool treat_as_symmetric = false)}{Same \lafunc{void gemv(const T beta, const SparseMat<T> &a, const char trans, const T alpha, const NRVec &x, const bool treat_as_symmetric = false)}{Same
for a sparse matrix (defined in \texttt{sparsemat.cc}, see Chapter~\ref{ch:sparse}); with \code{treat_as_symmetric} only one for a sparse matrix (defined in \texttt{sparsemat.cc}, see Chapter~\ref{ch:sparse}); with \code{treat_as_symmetric} only one
triangle is assumed to be stored.} triangle is assumed to be stored.}
@@ -172,7 +174,7 @@ real and imaginary parts are processed separately.}
\lafunc{const NRVec operator*(const NRMat<T> &mat) const}{Row vector times matrix, $x\Tr A$ (gemv with \code{'t'}; no conjugation). \lafunc{const NRVec operator*(const NRMat<T> &mat) const}{Row vector times matrix, $x\Tr A$ (gemv with \code{'t'}; no conjugation).
For $A x$ see \code{NRMat::operator*} in Chapter~\ref{ch:mat}.} For $A x$ see \code{NRMat::operator*} in Chapter~\ref{ch:mat}.}
\lafunc{const NRVec operator*(const NRSMat<T> &mat) const}{Row vector times packed symmetric/hermitian matrix, $x\Tr S$ (for hermitian $S$ computed as $S\Tr x = S^* x$ by \code{gemv} with \code{'t'}).} \lafunc{const NRVec operator*(const NRSMat<T> &mat) const}{Row vector times packed symmetric/hermitian matrix, $x\Tr S$ (for hermitian $S$ computed as $S\Tr x = S^* x$ by \code{gemv} with \code{'t'}).}
\lafunc{const NRVec operator*(const SparseMat<T> &mat) const}{Row vector times sparse matrix; CPU only.} \lafunc{const NRVec operator*(const SparseMat<T> &mat) const; const NRVec operator*(const CSRMat<T> &mat) const}{Row vector times sparse matrix; CPU only.}
\lafunc{const NRMat<T> otimes(const NRVec<T> &rhs, const bool conjugate = false, const T &scale = 1) const}{Outer product \lafunc{const NRMat<T> otimes(const NRVec<T> &rhs, const bool conjugate = false, const T &scale = 1) const}{Outer product
$A_{ij}=\mathrm{scale}\cdot x_i\, y_j$, with $y_j^*$ if \code{conjugate} (complex only); BLAS ger/geru/gerc.} $A_{ij}=\mathrm{scale}\cdot x_i\, y_j$, with $y_j^*$ if \code{conjugate} (complex only); BLAS ger/geru/gerc.}
\lafunc{const NRMat<T> operator|(const NRVec<T> &rhs) const}{Same as \code{otimes(rhs,true)}, i.e.\ $x y\Hc$.} \lafunc{const NRMat<T> operator|(const NRVec<T> &rhs) const}{Same as \code{otimes(rhs,true)}, i.e.\ $x y\Hc$.}
+28
View File
@@ -32,6 +32,19 @@ while((unsigned char)i!=terminator&&(unsigned char)j!=terminator&&(unsigned char
} }
} }
template <>
void fourindex<unsigned char,float>::fscanf(FILE *ff)
{
int i,j,k,l;
float elem;
if(5!= ::fscanf(ff,"%f %d %d %d %d",&elem,&i,&j,&k,&l)) laerror("read error in fourindex::fscanf");
while((unsigned char)i!=terminator&&(unsigned char)j!=terminator&&(unsigned char)k!=terminator&&(unsigned char)l!=terminator) //cast: -1 in the file matches the default terminator
{
add(i,j,k,l,elem);
if(5!= ::fscanf(ff,"%f %d %d %d %d",&elem,&i,&j,&k,&l)) laerror("read error in fourindex::fscanf");
}
}
template <> template <>
void fourindex<unsigned char,double>::fprintf(FILE *f, char *format) const void fourindex<unsigned char,double>::fprintf(FILE *f, char *format) const
{ {
@@ -44,6 +57,18 @@ while(it!=end)
::fprintf(f,format,0.,(int)terminator,(int)terminator,(int)terminator,(int)terminator); //zero line for terminator 0 (FCIDUMP-like) ::fprintf(f,format,0.,(int)terminator,(int)terminator,(int)terminator,(int)terminator); //zero line for terminator 0 (FCIDUMP-like)
} }
template <>
void fourindex<unsigned char,float>::fprintf(FILE *f, char *format) const
{
fourindex<unsigned char,float>::iterator it=this->begin(),end=this->end();
while(it!=end)
{
::fprintf(f,format,it->elem,it->index.indiv.i,it->index.indiv.j,it->index.indiv.k,it->index.indiv.l);
++it;
}
::fprintf(f,format,0.,(int)terminator,(int)terminator,(int)terminator,(int)terminator); //zero line for terminator 0 (FCIDUMP-like)
}
/***************************************************************************//** /***************************************************************************//**
@@ -52,4 +77,7 @@ while(it!=end)
template class fourindex<unsigned char,double>; template class fourindex<unsigned char,double>;
template class fourindex_ext<unsigned char,double>; template class fourindex_ext<unsigned char,double>;
template class fourindex_dense<nosymmetry,double,unsigned char>; template class fourindex_dense<nosymmetry,double,unsigned char>;
template class fourindex<unsigned char,float>;
template class fourindex_ext<unsigned char,float>;
template class fourindex_dense<nosymmetry,float,unsigned char>;
} }
+1 -1
View File
@@ -73,7 +73,7 @@ for(int i=0;i<=k;i++)
//x contains ev. initial guess and on return the solution //x contains ev. initial guess and on return the solution
template<typename T, typename Matrix> template<typename T, typename Matrix>
bool gmres(const Matrix &bigmat, const NRVec<T> &b, NRVec<T> &x, const bool doguess=1, const double eps=1e-7, const int MAXIT=50, const bool verbose=1, bool square=1,const bool precondition=1, int neustart=0, const int incore=1) bool gmres(const Matrix &bigmat, const NRVec<T> &b, NRVec<T> &x, const bool doguess=1, const double eps=LA_precision<typename LA_traits<T>::normtype>::gmres_eps(), const int MAXIT=50, const bool verbose=1, bool square=1,const bool precondition=1, int neustart=0, const int incore=1)
{ {
int zeilen=bigmat.nrows(); int zeilen=bigmat.nrows();
int spalten=bigmat.ncols(); int spalten=bigmat.ncols();
+30
View File
@@ -308,6 +308,7 @@ static inline C realpart(const std::complex<C> &x) {return x.real();}
static inline C imagpart(const std::complex<C> &x) {return x.imag();} static inline C imagpart(const std::complex<C> &x) {return x.imag();}
static inline void setrealpart(std::complex<C> &x, const C &y) {reinterpret_cast<C(&)[2]>(x)[0]=y;} static inline void setrealpart(std::complex<C> &x, const C &y) {reinterpret_cast<C(&)[2]>(x)[0]=y;}
static inline void setimagpart(std::complex<C> &x, const C &y) {reinterpret_cast<C(&)[2]>(x)[1]=y;} static inline void setimagpart(std::complex<C> &x, const C &y) {reinterpret_cast<C(&)[2]>(x)[1]=y;}
static int strassen_cutoff() {return 2048;} //NRMat::strassen uses plain gemm for blocks with a dimension <= cutoff
}; };
@@ -368,6 +369,7 @@ static inline C realpart(const C &x) {return x;}
static inline C imagpart(const C &x) {return 0;} static inline C imagpart(const C &x) {return 0;}
static inline void setrealpart(C &x, const C &y) {x=y;} static inline void setrealpart(C &x, const C &y) {x=y;}
static inline void setimagpart(C &x, const C &y) {} static inline void setimagpart(C &x, const C &y) {}
static int strassen_cutoff() {return 2048;} //NRMat::strassen uses plain gemm for blocks with a dimension <= cutoff
}; };
@@ -456,6 +458,34 @@ generate_traits_smat(NRSMat_from1)
template<typename C> template<typename C>
struct LA_traits : LA_traits_aux<C, typename isscalar<C>::scalar_type> {}; struct LA_traits : LA_traits_aux<C, typename isscalar<C>::scalar_type> {};
//precision-dependent default thresholds; only float and double are supported (the double values are those used so far)
template<typename T> struct LA_precision;
template<> struct LA_precision<double>
{
static double svd_threshold() {return 1e-12;} //singular values treated as zero (svd_solve, Tucker)
static double taylor_precision() {return 1e-14;} //truncation of the Taylor series in matexp
static double conjgrad_tol() {return 1e-8;} //default convergence threshold of conjgrad
static double gmres_eps() {return 1e-7;} //default relative residual of gmres
static double eigensolver_eps() {return 1e-6;} //default convergence threshold of davidson and lanczos
static double newton_threshold() {return 1e-14;} //convergence of Polynomial::newton
static double det_tiny() {return 1e-300;} //determinant underflow guard in linear_solve
static double sparse_epsilon() {return 1e-15;} //elements treated as zero in the sparse matrices (SPARSEEPSILON)
static double cholesky_epsilon() {return 1e-16;} //pivot threshold of the sparse Cholesky (CHOLESKYEPSILON)
};
template<> struct LA_precision<float>
{
static float svd_threshold() {return 1e-5f;}
static float taylor_precision() {return 1e-7f;}
static float conjgrad_tol() {return 1e-5f;}
static float gmres_eps() {return 1e-5f;}
static float eigensolver_eps() {return 1e-4f;}
static float newton_threshold() {return 1e-6f;}
static float det_tiny() {return 1e-37f;}
static float sparse_epsilon() {return 1e-7f;}
static float cholesky_epsilon() {return 1e-8f;}
};
}//namespace }//namespace
#endif #endif
+1 -1
View File
@@ -149,7 +149,7 @@ throw LAerror(s1);
} }
//stub for f77 blas called from strassen routine //stub for f77 blas error handler
extern "C" void xerbla_(const char name[6], int *n){ extern "C" void xerbla_(const char name[6], int *n){
char msg[1024]; char msg[1024];
strcpy(msg,"LAPACK or BLAS error in routine "); strcpy(msg,"LAPACK or BLAS error in routine ");
+1 -1
View File
@@ -39,7 +39,7 @@ namespace LA {
template <typename T, typename Matrix> template <typename T, typename Matrix>
extern void lanczos(const Matrix &bigmat, NRVec<T> &eivals, NRVec<T> *eivecs, const char *eivecsfile, extern void lanczos(const Matrix &bigmat, NRVec<T> &eivals, NRVec<T> *eivecs, const char *eivecsfile,
int nroots=1, const bool verbose=0, const double eps=1e-6, int nroots=1, const bool verbose=0, const double eps=LA_precision<typename LA_traits<T>::normtype>::eigensolver_eps(),
const bool incore=1, int maxit=100, const int maxkrylov = 1000, const bool incore=1, int maxit=100, const int maxkrylov = 1000,
void (*initguess)(NRVec<T> &)=NULL, const typename LA_traits<T>::normtype *target=NULL) void (*initguess)(NRVec<T> &)=NULL, const typename LA_traits<T>::normtype *target=NULL)
{ {
+1933 -2
View File
File diff suppressed because it is too large. Load diff
+124 -5
View File
@@ -424,9 +424,13 @@ public:
NRMat & operator+=(const SparseMat<T> &rhs); NRMat & operator+=(const SparseMat<T> &rhs);
//! subtract given sparse matrix //! subtract given sparse matrix
NRMat & operator-=(const SparseMat<T> &rhs); NRMat & operator-=(const SparseMat<T> &rhs);
NRMat & operator+=(const CSRMat<T> &rhs);
NRMat & operator-=(const CSRMat<T> &rhs);
//! perform the \b gemm operation //! perform the \b gemm operation
void gemm(const T &beta, const SparseMat<T> &a, const char transa, const NRMat &b, const char transb, const T &alpha); void gemm(const T &beta, const SparseMat<T> &a, const char transa, const NRMat &b, const char transb, const T &alpha);
void gemm(const T &beta, const CSRMat<T> &a, const char transa, const NRMat &b, const char transb, const T &alpha);
const NRMat operator*(const CSRMat<T> &rhs) const; //dense times compressed row sparse, dense result
inline void simplify() {}; inline void simplify() {};
bool issymmetric() const { return 0; }; bool issymmetric() const { return 0; };
@@ -438,11 +442,8 @@ public:
void printsorted(std::ostream &s, int direction=1, bool absvalue=false, bool stable=false, typename LA_traits<T>::normtype thr=0, int topmax=0) const; void printsorted(std::ostream &s, int direction=1, bool absvalue=false, bool stable=false, typename LA_traits<T>::normtype thr=0, int topmax=0) const;
#ifndef NO_STRASSEN //! Strassen-Winograd multiplication (\f$\mathcal{O}(n^{2.81})\f$), the same operation as \see NRMat<T>::gemm(); blocks with a dimension <= cutoff are multiplied by gemm
//! Strassen's multiplication (better than \f$\mathacal{O}(n^3)\f$, analogous syntax to \see NRMat<T>::gemm() ) void strassen(const T &beta, const NRMat &a, const char transa, const NRMat &b, const char transb, const T &alpha, const int cutoff=LA_traits<T>::strassen_cutoff());
void strassen(const T beta, const NRMat &a, const char transa, const NRMat &b, const char transb, const T alpha);
void s_cutoff(const int,const int,const int,const int) const;
#endif
void swap(NRMat &rhs) //more efficient swap than via tmp and constructors and operator= void swap(NRMat &rhs) //more efficient swap than via tmp and constructors and operator=
{ {
int tmpnn=nn; nn=rhs.nn; rhs.nn=tmpnn; int tmpnn=nn; nn=rhs.nn; rhs.nn=tmpnn;
@@ -970,6 +971,27 @@ inline const double NRMat<double>::amax() const{
#endif #endif
} }
template<>
inline const float NRMat<float>::amax() const{
#ifdef CUDALA
if(location == cpu){
#endif
#ifdef MATPTR
return v[0][cblas_isamax(nn*mm, v[0], 1) ];
#else
return v[cblas_isamax(nn*mm, v, 1) ];
#endif
#ifdef CUDALA
}else{
float ret(0.0);
const size_t pozice = cublasIsamax((size_t)nn*mm, v, 1);
TEST_CUBLAS("cublasIsamax");
gpuget(1, sizeof(float), v + pozice, &ret);
return ret;
}
#endif
}
/***************************************************************************//** /***************************************************************************//**
* for this real matrix \f$A\f$, determine the first element * for this real matrix \f$A\f$, determine the first element
* with smallest absolute value * with smallest absolute value
@@ -1009,6 +1031,40 @@ inline const double NRMat<double>::amin() const{
return ret; return ret;
} }
template<>
inline const float NRMat<float>::amin() const{
float ret(0.0);
#ifdef CUDALA
if(location == cpu){
#endif
// idamin seems not to be supported
const size_t nm = (size_t)nn*mm;
float val(0.0);
int index(-1);
ret = std::numeric_limits<float>::max();
for(register int i=0; i < nm; i++){
#ifdef MATPTR
val = std::abs(v[0][i]);
#else
val = std::abs(v[i]);
#endif
if(val < ret){ index = i; ret = val; }
}
#ifdef MATPTR
ret = v[0][index];
#else
ret = v[index];
#endif
#ifdef CUDALA
}else{
const size_t pozice = cublasIsamin((size_t)nn*mm, v, 1);
TEST_CUBLAS("cublasIsamin");
gpuget(1, sizeof(float), v + pozice, &ret);
}
#endif
return ret;
}
/***************************************************************************//** /***************************************************************************//**
* for this complex matrix \f$A\f$, determine the smallest index of the maximum * for this complex matrix \f$A\f$, determine the smallest index of the maximum
* magnitude element, i.e. maximal element in the 1-norm * magnitude element, i.e. maximal element in the 1-norm
@@ -1036,6 +1092,28 @@ inline const std::complex<double> NRMat<std::complex<double> >::amax() const{
} }
template<>
inline const std::complex<float> NRMat<std::complex<float> >::amax() const{
#ifdef CUDALA
if(location == cpu){
#endif
#ifdef MATPTR
return v[0][cblas_icamax(nn*mm, v[0], 1) ];
#else
return v[cblas_icamax(nn*mm, v, 1) ];
#endif
#ifdef CUDALA
}else{
std::complex<float> ret(0.0, 0.0);
const size_t pozice = cublasIcamax((size_t)nn*mm, (cuComplex*)v, 1);
TEST_CUBLAS("cublasIcamax");
gpuget(1, sizeof(std::complex<float>), v + pozice, &ret);
return ret;
}
#endif
}
/***************************************************************************//** /***************************************************************************//**
* for this complex matrix \f$A\f$, determine the smallest index of the minimum * for this complex matrix \f$A\f$, determine the smallest index of the minimum
* magnitude element, i.e. minimal element in the 1-norm * magnitude element, i.e. minimal element in the 1-norm
@@ -1078,6 +1156,43 @@ inline const std::complex<double> NRMat<std::complex<double> >::amin() const{
return ret; return ret;
} }
template<>
inline const std::complex<float> NRMat<std::complex<float> >::amin() const{
std::complex<float> ret(0.0, 0.0);
#ifdef CUDALA
if(location == cpu){
#endif
// idamin seems not to be supported
const size_t nm = (size_t)nn*mm;
int index(-1);
float val(0.0), min_val(0.0);
std::complex<float> z_val(0.0, 0.0);
min_val = std::numeric_limits<float>::max();
for(register int i=0; i < nm; i++){
#ifdef MATPTR
z_val = v[0][i];
#else
z_val = v[i];
#endif
val = std::abs(z_val.real()) + std::abs(z_val.imag());
if(val < min_val){ index = i; min_val = val; }
}
#ifdef MATPTR
ret = v[0][index];
#else
ret = v[index];
#endif
#ifdef CUDALA
}else{
const size_t pozice = cublasIcamin((size_t)nn*mm, (cuComplex*)v, 1);
TEST_CUBLAS("cublasIcamin");
gpuget(1, sizeof(std::complex<float>), v + pozice, &ret);
}
#endif
return ret;
}
/***************************************************************************//** /***************************************************************************//**
* destructor for general type * destructor for general type
* @see NRMat<T>::count * @see NRMat<T>::count
@@ -1340,6 +1455,10 @@ NRMat<std::complex<T> > complexify(const NRMat<T> &rhs) {
return r; return r;
} }
//BLAS versions for double and float are in mat.cc
template<> NRMat<std::complex<double> > complexify<double>(const NRMat<double> &rhs);
template<> NRMat<std::complex<float> > complexify<float>(const NRMat<float> &rhs);
//this is general for any type, complexmatrix() uses blas for doubles //this is general for any type, complexmatrix() uses blas for doubles
template<typename T> template<typename T>
NRMat<std::complex<T> > complexify(const NRMat<T> &rhsr, const NRMat<T> &rhsi) { NRMat<std::complex<T> > complexify(const NRMat<T> &rhsr, const NRMat<T> &rhsi) {
+2 -2
View File
@@ -274,7 +274,7 @@ double scale=std::exp(-std::log(2.)*power);
//find how long taylor expansion will be necessary //find how long taylor expansion will be necessary
const double precision=1e-14; //further decreasing brings nothing const double precision=LA_precision<typename LA_traits<C>::normtype>::taylor_precision(); //further decreasing brings nothing
double s,t; double s,t;
s=mnorm*scale; s=mnorm*scale;
int n=0; int n=0;
@@ -311,7 +311,7 @@ if(maxpower>=0 && power>maxpower) power=maxpower;
double scale=std::exp(-std::log(2.)*power); double scale=std::exp(-std::log(2.)*power);
//find how long taylor expansion will be necessary //find how long taylor expansion will be necessary
const double precision=1e-14; //further decreasing brings nothing const double precision=LA_precision<typename LA_traits<C>::normtype>::taylor_precision(); //further decreasing brings nothing
double s,t; double s,t;
s=mnorm*scale; s=mnorm*scale;
int n=0; int n=0;
+313 -1
View File
@@ -185,6 +185,18 @@ void cblas_zcopy(const int N, const void *X, const int incX, void *Y, const int
#endif #endif
} }
extern "C" void FORNAME(ccopy) (const FINT *n, const void *x, const FINT *incx, void *y, const FINT *incy);
void cblas_ccopy(const int N, const void *X, const int incX, void *Y, const int incY){
#ifdef FORINT
const FINT ntmp=N;
const FINT incxtmp=incX;
const FINT incytmp=incY;
FORNAME(ccopy) (&ntmp,X,&incxtmp,Y,&incytmp);
#else
FORNAME(ccopy) (&N,X,&incX,Y,&incY);
#endif
}
extern "C" void FORNAME(zaxpy) (const FINT *n, const void *a, const void *x, const FINT *incx, void *y, const FINT *incy); extern "C" void FORNAME(zaxpy) (const FINT *n, const void *a, const void *x, const FINT *incx, void *y, const FINT *incy);
void cblas_zaxpy(const int N, const void *alpha, const void *X, const int incX, void *Y, const int incY){ void cblas_zaxpy(const int N, const void *alpha, const void *X, const int incX, void *Y, const int incY){
#ifdef FORINT #ifdef FORINT
@@ -197,6 +209,18 @@ void cblas_zaxpy(const int N, const void *alpha, const void *X, const int incX,
#endif #endif
} }
extern "C" void FORNAME(caxpy) (const FINT *n, const void *a, const void *x, const FINT *incx, void *y, const FINT *incy);
void cblas_caxpy(const int N, const void *alpha, const void *X, const int incX, void *Y, const int incY){
#ifdef FORINT
const FINT ntmp=N;
const FINT incxtmp=incX;
const FINT incytmp=incY;
FORNAME(caxpy) (&ntmp,alpha,X,&incxtmp,Y,&incytmp);
#else
FORNAME(caxpy) (&N,alpha,X,&incX,Y,&incY);
#endif
}
extern "C" void FORNAME(zscal) (const FINT *n, const void *a, void *x, const FINT *incx); extern "C" void FORNAME(zscal) (const FINT *n, const void *a, void *x, const FINT *incx);
void cblas_zscal(const int N, const void *alpha, void *X, const int incX){ void cblas_zscal(const int N, const void *alpha, void *X, const int incX){
#ifdef FORINT #ifdef FORINT
@@ -208,6 +232,17 @@ void cblas_zscal(const int N, const void *alpha, void *X, const int incX){
#endif #endif
} }
extern "C" void FORNAME(cscal) (const FINT *n, const void *a, void *x, const FINT *incx);
void cblas_cscal(const int N, const void *alpha, void *X, const int incX){
#ifdef FORINT
const FINT ntmp=N;
const FINT incxtmp=incX;
FORNAME(cscal)(&ntmp,alpha,X,&incxtmp);
#else
FORNAME(cscal)(&N,alpha,X,&incX);
#endif
}
extern "C" void FORNAME(zdscal) (const FINT *n, const double *a, void *x, const FINT *incx); extern "C" void FORNAME(zdscal) (const FINT *n, const double *a, void *x, const FINT *incx);
void cblas_zdscal(const int N, const double alpha, void *X, const int incX){ void cblas_zdscal(const int N, const double alpha, void *X, const int incX){
#ifdef FORINT #ifdef FORINT
@@ -219,6 +254,17 @@ void cblas_zdscal(const int N, const double alpha, void *X, const int incX){
#endif #endif
} }
extern "C" void FORNAME(csscal) (const FINT *n, const float *a, void *x, const FINT *incx);
void cblas_csscal(const int N, const float alpha, void *X, const int incX){
#ifdef FORINT
const FINT ntmp=N;
const FINT incxtmp=incX;
FORNAME(csscal)(&ntmp,&alpha,X,&incxtmp);
#else
FORNAME(csscal)(&N,&alpha,X,&incX);
#endif
}
extern "C" double FORNAME(dznrm2) (const FINT *n, const void *x, const FINT *incx); extern "C" double FORNAME(dznrm2) (const FINT *n, const void *x, const FINT *incx);
double cblas_dznrm2(const int N, const void *X, const int incX){ double cblas_dznrm2(const int N, const void *X, const int incX){
@@ -294,6 +340,54 @@ void cblas_zdotc_sub(const int N, const void *X, const int incX, const void *Y,
} }
#ifdef F2C_COMPLEX_RETURN
extern "C" void FORNAME(cdotu) (void *retval, const FINT *n, const void *x, const FINT *incx, const void *y, const FINT *incy);
extern "C" void FORNAME(cdotc) (void *retval, const FINT *n, const void *x, const FINT *incx, const void *y, const FINT *incy);
#else
extern "C" std::complex<float> FORNAME(cdotu) (const FINT *n, const void *x, const FINT *incx, const void *y, const FINT *incy);
extern "C" std::complex<float> FORNAME(cdotc) (const FINT *n, const void *x, const FINT *incx, const void *y, const FINT *incy);
#endif
void cblas_cdotu_sub(const int N, const void *X, const int incX, const void *Y, const int incY, void *dotu){
#ifdef FORINT
const FINT ntmp=N;
const FINT incxtmp=incX;
const FINT incytmp=incY;
#ifdef F2C_COMPLEX_RETURN
FORNAME(cdotu) (dotu,&ntmp,X,&incxtmp,Y,&incytmp);
#else
*(std::complex<float> *)dotu = FORNAME(cdotu) (&ntmp,X,&incxtmp,Y,&incytmp);
#endif
#else
#ifdef F2C_COMPLEX_RETURN
FORNAME(cdotu) (dotu,&N,X,&incX,Y,&incY);
#else
*(std::complex<float> *)dotu = FORNAME(cdotu) (&N,X,&incX,Y,&incY);
#endif
#endif
}
void cblas_cdotc_sub(const int N, const void *X, const int incX, const void *Y, const int incY, void *dotc){
#ifdef FORINT
const FINT ntmp=N;
const FINT incxtmp=incX;
const FINT incytmp=incY;
#ifdef F2C_COMPLEX_RETURN
FORNAME(cdotc) (dotc,&ntmp,X,&incxtmp,Y,&incytmp);
#else
*(std::complex<float> *)dotc = FORNAME(cdotc) (&ntmp,X,&incxtmp,Y,&incytmp);
#endif
#else
#ifdef F2C_COMPLEX_RETURN
FORNAME(cdotc) (dotc,&N,X,&incX,Y,&incY);
#else
*(std::complex<float> *)dotc = FORNAME(cdotc) (&N,X,&incX,Y,&incY);
#endif
#endif
}
//Level 2 and Level 3 on symmetric/hermitian packed matrices - straightforward //Level 2 and Level 3 on symmetric/hermitian packed matrices - straightforward
//enum CBLAS_UPLO {CblasUpper=121, CblasLower=122}; //enum CBLAS_UPLO {CblasUpper=121, CblasLower=122};
//enum CBLAS_ORDER {CblasRowMajor=101, CblasColMajor=102 }; //enum CBLAS_ORDER {CblasRowMajor=101, CblasColMajor=102 };
@@ -382,6 +476,47 @@ p= (std::complex<double> *) X; for(int i=0; i<N; ++i) {p->imag(-p->imag()); p+=
p= (std::complex<double> *) Y; for(int i=0; i<N; ++i) {p->imag(-p->imag()); p+= incY;} p= (std::complex<double> *) Y; for(int i=0; i<N; ++i) {p->imag(-p->imag()); p+= incY;}
} }
extern "C" void FORNAME(chpmv) (const char *uplo, const FINT *n, const void *alpha, const void *ap, const void *x, const FINT *incx, const void *beta, void *y, const FINT *incy);
void cblas_chpmv(const CBLAS_ORDER Order, const CBLAS_UPLO Uplo,
const int N, const void *alpha, const void *Ap,
const void *X, const int incX,
const void *beta, void *Y, const int incY)
{
char U = BLAS_FORTRANCASE('u');
if(Order==CblasColMajor && Uplo==CblasUpper) //native fortran convention, no conjugation needed
{
#ifdef FORINT
const FINT ntmp=N;
const FINT incxtmp=incX;
const FINT incytmp=incY;
FORNAME(chpmv) (&U,&ntmp, alpha, Ap, X, &incxtmp, beta, Y, &incytmp);
#else
FORNAME(chpmv) (&U,&N, alpha, Ap, X, &incX, beta, Y, &incY);
#endif
return;
}
if(Order!=CblasRowMajor) laerror("CblasRowMajor or CblasColMajor+CblasUpper order asserted");
if(Uplo!=CblasLower) laerror("CblasLower uplo asserted");
//row-major lower packed hermitian H is column-major upper packed conj(H), therefore
//conj(y) = conj(beta) conj(y) + conj(alpha) conj(H) conj(x) is computed by chpmv on conjugated vectors and scalars
std::complex<float> alphac = std::conj(*(const std::complex<float> *)alpha);
std::complex<float> betac = std::conj(*(const std::complex<float> *)beta);
std::complex<float> *p;
p= (std::complex<float> *) X; for(int i=0; i<N; ++i) {p->imag(-p->imag()); p+= incX;}
p= (std::complex<float> *) Y; for(int i=0; i<N; ++i) {p->imag(-p->imag()); p+= incY;}
#ifdef FORINT
const FINT ntmp=N;
const FINT incxtmp=incX;
const FINT incytmp=incY;
FORNAME(chpmv) (&U,&ntmp, &alphac, Ap, X, &incxtmp, &betac, Y, &incytmp);
#else
FORNAME(chpmv) (&U,&N, &alphac, Ap, X, &incX, &betac, Y, &incY);
#endif
//conjugate back
p= (std::complex<float> *) X; for(int i=0; i<N; ++i) {p->imag(-p->imag()); p+= incX;}
p= (std::complex<float> *) Y; for(int i=0; i<N; ++i) {p->imag(-p->imag()); p+= incY;}
}
extern "C" void FORNAME(dspr2) (const char *uplo, const FINT *n, const double *alpha, const double *x, const FINT *incx, const double *y, const FINT *incy, double *ap); extern "C" void FORNAME(dspr2) (const char *uplo, const FINT *n, const double *alpha, const double *x, const FINT *incx, const double *y, const FINT *incy, double *ap);
void cblas_dspr2(const CBLAS_ORDER Order, const CBLAS_UPLO Uplo, void cblas_dspr2(const CBLAS_ORDER Order, const CBLAS_UPLO Uplo,
@@ -401,6 +536,24 @@ void cblas_dspr2(const CBLAS_ORDER Order, const CBLAS_UPLO Uplo,
#endif #endif
} }
extern "C" void FORNAME(sspr2) (const char *uplo, const FINT *n, const float *alpha, const float *x, const FINT *incx, const float *y, const FINT *incy, float *ap);
void cblas_sspr2(const CBLAS_ORDER Order, const CBLAS_UPLO Uplo,
const int N, const float alpha, const float *X,
const int incX, const float *Y, const int incY, float *Ap)
{
if(Order!=CblasRowMajor) laerror("CblasRowMajor order asserted");
if(Uplo!=CblasLower) laerror("CblasLower uplo asserted");
char U = BLAS_FORTRANCASE('u');
#ifdef FORINT
const FINT ntmp=N;
const FINT incxtmp=incX;
const FINT incytmp=incY;
FORNAME(sspr2) (&U,&ntmp, &alpha, X, &incxtmp, Y, &incytmp, Ap);
#else
FORNAME(sspr2) (&U,&N, &alpha, X, &incX, Y, &incY, Ap);
#endif
}
//row-major lower packed hermitian H is column-major upper packed conj(H), therefore //row-major lower packed hermitian H is column-major upper packed conj(H), therefore
//conj(H) += conj(a) conj(x) conj(y)^H + a conj(y) conj(x)^H is computed by zhpr2 with alpha=a and conjugated swapped vectors //conj(H) += conj(a) conj(x) conj(y)^H + a conj(y) conj(x)^H is computed by zhpr2 with alpha=a and conjugated swapped vectors
@@ -430,6 +583,32 @@ p= (std::complex<double> *) X; for(int i=0; i<N; ++i) {p->imag(-p->imag()); p+=
if(!same) {p= (std::complex<double> *) Y; for(int i=0; i<N; ++i) {p->imag(-p->imag()); p+= incY;}} if(!same) {p= (std::complex<double> *) Y; for(int i=0; i<N; ++i) {p->imag(-p->imag()); p+= incY;}}
} }
extern "C" void FORNAME(chpr2) (const char *uplo, const FINT *n, const void *alpha, const void *x, const FINT *incx, const void *y, const FINT *incy, void *ap);
void cblas_chpr2(const CBLAS_ORDER Order, const CBLAS_UPLO Uplo, const int N,
const void *alpha, const void *X, const int incX,
const void *Y, const int incY, void *Ap)
{
if(Order!=CblasRowMajor) laerror("CblasRowMajor order asserted");
if(Uplo!=CblasLower) laerror("CblasLower uplo asserted");
char U = BLAS_FORTRANCASE('u');
bool same = (X==Y && incX==incY);
//conjugate x and y
std::complex<float> *p;
p= (std::complex<float> *) X; for(int i=0; i<N; ++i) {p->imag(-p->imag()); p+= incX;}
if(!same) {p= (std::complex<float> *) Y; for(int i=0; i<N; ++i) {p->imag(-p->imag()); p+= incY;}}
#ifdef FORINT
const FINT ntmp=N;
const FINT incxtmp=incX;
const FINT incytmp=incY;
FORNAME(chpr2) (&U,&ntmp, alpha, Y, &incytmp, X, &incxtmp, Ap);
#else
FORNAME(chpr2) (&U,&N, alpha, Y, &incY, X, &incX, Ap);
#endif
//conjugate back
p= (std::complex<float> *) X; for(int i=0; i<N; ++i) {p->imag(-p->imag()); p+= incX;}
if(!same) {p= (std::complex<float> *) Y; for(int i=0; i<N; ++i) {p->imag(-p->imag()); p+= incY;}}
}
//Level 2 and Level 3 on general matrices - take into account the transposed storage of matrices in Fortran and C //Level 2 and Level 3 on general matrices - take into account the transposed storage of matrices in Fortran and C
@@ -516,6 +695,47 @@ void cblas_zgeru(const CBLAS_ORDER Order, const int M, const int N,
#endif #endif
} }
extern "C" void FORNAME(cgeru) (const FINT *m, const FINT *n, const void *alpha, const void *x, const FINT *incx, const void *y, const FINT *incy, void *a, const FINT *lda);
void cblas_cgerc(const CBLAS_ORDER Order, const int M, const int N,
const void *alpha, const void *X, const int incX,
const void *Y, const int incY, void *A, const int lda)
{
if(Order!=CblasRowMajor) laerror("CblasRowMajor order asserted");
//conjugate y
std::complex<float> *p;
p= (std::complex<float> *) Y; for(int i=0; i<N; ++i) {p->imag(-p->imag()); p+= incY;}
#ifdef FORINT
const FINT mtmp=M;
const FINT ntmp=N;
const FINT incxtmp=incX;
const FINT incytmp=incY;
const FINT ldatmp=lda;
FORNAME(cgeru) (&ntmp, &mtmp, alpha, Y, &incytmp, X, &incxtmp, A, &ldatmp);
#else
FORNAME(cgeru)(&N, &M, alpha, Y, &incY, X, &incX, A, &lda);
#endif
//conjugate y back
p= (std::complex<float> *) Y; for(int i=0; i<N; ++i) {p->imag(-p->imag()); p+= incY;}
}
void cblas_cgeru(const CBLAS_ORDER Order, const int M, const int N,
const void *alpha, const void *X, const int incX,
const void *Y, const int incY, void *A, const int lda)
{
if(Order!=CblasRowMajor) laerror("CblasRowMajor order asserted");
#ifdef FORINT
const FINT mtmp=M;
const FINT ntmp=N;
const FINT incxtmp=incX;
const FINT incytmp=incY;
const FINT ldatmp=lda;
FORNAME(cgeru) (&ntmp, &mtmp, alpha, Y, &incytmp, X, &incxtmp, A, &ldatmp);
#else
FORNAME(cgeru)(&N, &M, alpha, Y, &incY, X, &incX, A, &lda);
#endif
}
extern "C" void FORNAME(dgemm) (const char *transa, const char *transb, const FINT *m, const FINT *n, const FINT *k, const double *alpha, const double *a, const FINT *lda, const double *b, const FINT *ldb, const double *beta, double *c, const FINT *ldc); extern "C" void FORNAME(dgemm) (const char *transa, const char *transb, const FINT *m, const FINT *n, const FINT *k, const double *alpha, const double *a, const FINT *lda, const double *b, const FINT *ldb, const double *beta, double *c, const FINT *ldc);
void cblas_dgemm(const CBLAS_ORDER Order, const CBLAS_TRANSPOSE TransA, void cblas_dgemm(const CBLAS_ORDER Order, const CBLAS_TRANSPOSE TransA,
@@ -595,6 +815,32 @@ void cblas_zgemm(const CBLAS_ORDER Order, const CBLAS_TRANSPOSE TransA,
#endif #endif
} }
extern "C" void FORNAME(cgemm) (const char *transa, const char *transb, const FINT *m, const FINT *n, const FINT *k, const void *alpha, const void *a, const FINT *lda, const void *b, const FINT *ldb, const void *beta, void *c, const FINT *ldc);
void cblas_cgemm(const CBLAS_ORDER Order, const CBLAS_TRANSPOSE TransA,
const CBLAS_TRANSPOSE TransB, const int M, const int N,
const int K, const void *alpha, const void *A,
const int lda, const void *B, const int ldb,
const void *beta, void *C, const int ldc)
{
if(Order!=CblasRowMajor) laerror("CblasRowMajor order asserted");
//swap a-b, m-n
char transb = BLAS_FORTRANCASE(TransB==CblasConjTrans?'C':(TransB==CblasNoTrans?'N':'T'));
char transa = BLAS_FORTRANCASE(TransA==CblasConjTrans?'C':(TransA==CblasNoTrans?'N':'T'));
#ifdef FORINT
const FINT mtmp=M;
const FINT ntmp=N;
const FINT ktmp=K;
const FINT ldatmp=lda;
const FINT ldbtmp=ldb;
const FINT ldctmp=ldc;
FORNAME(cgemm) (&transb,&transa,
&ntmp, &mtmp, &ktmp, alpha, B, &ldbtmp, A, &ldatmp, beta, C, &ldctmp);
#else
FORNAME(cgemm) ( &transb,&transa,
&N, &M, &K, alpha, B, &ldb, A, &lda, beta, C, &ldc);
#endif
}
extern "C" void FORNAME(dgemv) (const char *TRANS, const FINT *M, const FINT *N, const double *ALPHA, const double *A, const FINT *LDA, const double *X, const FINT *INCX, const double *BETA, double *Y, const FINT *INCY); extern "C" void FORNAME(dgemv) (const char *TRANS, const FINT *M, const FINT *N, const double *ALPHA, const double *A, const FINT *LDA, const double *X, const FINT *INCX, const double *BETA, double *Y, const FINT *INCY);
void cblas_dgemv(const CBLAS_ORDER Order, void cblas_dgemv(const CBLAS_ORDER Order,
@@ -691,6 +937,50 @@ if(TransA == CblasConjTrans)
#endif #endif
} }
extern "C" void FORNAME(cgemv) (const char *TRANS, const FINT *M, const FINT *N, const void *ALPHA, const void *A, const FINT *LDA, const void *X, const FINT *INCX, const void *BETA, void *Y, const FINT *INCY);
void cblas_cgemv(const CBLAS_ORDER Order,
const CBLAS_TRANSPOSE TransA, const int M, const int N,
const void *alpha, const void *A, const int lda,
const void *X, const int incX, const void *beta,
void *Y, const int incY)
{
if(Order!=CblasRowMajor) laerror("CblasRowMajor order asserted");
//swap n-m and toggle transposition
char transa = BLAS_FORTRANCASE(TransA==CblasNoTrans?'T':'N');
#ifdef FORINT
const FINT mtmp=M;
const FINT ntmp=N;
const FINT ldatmp=lda;
const FINT incxtmp=incX;
const FINT incytmp=incY;
#endif
if(TransA == CblasConjTrans)
{
//row-major A is column-major A^T, so A^H = conj(A^T) and
//conj(y) = conj(beta) conj(y) + conj(alpha) A^T conj(x) is computed by non-transposed cgemv on conjugated vectors and scalars
const int lx = M, ly = N; //lengths of x and y for op(A)=A^H
std::complex<float> alphac = std::conj(*(const std::complex<float> *)alpha);
std::complex<float> betac = std::conj(*(const std::complex<float> *)beta);
std::complex<float> *p;
p= (std::complex<float> *) X; for(int i=0; i<lx; ++i) {p->imag(-p->imag()); p+= incX;}
p= (std::complex<float> *) Y; for(int i=0; i<ly; ++i) {p->imag(-p->imag()); p+= incY;}
#ifdef FORINT
FORNAME(cgemv) (&transa, &ntmp, &mtmp, &alphac, A, &ldatmp, X, &incxtmp, &betac, Y, &incytmp );
#else
FORNAME(cgemv) (&transa, &N, &M, &alphac, A, &lda, X, &incX, &betac, Y, &incY );
#endif
//conjugate back
p= (std::complex<float> *) X; for(int i=0; i<lx; ++i) {p->imag(-p->imag()); p+= incX;}
p= (std::complex<float> *) Y; for(int i=0; i<ly; ++i) {p->imag(-p->imag()); p+= incY;}
return;
}
#ifdef FORINT
FORNAME(cgemv) (&transa, &ntmp, &mtmp, alpha, A, &ldatmp, X, &incxtmp, beta, Y, &incytmp );
#else
FORNAME(cgemv) (&transa, &N, &M, alpha, A, &lda, X, &incX, beta, Y, &incY );
#endif
}
extern "C" FINT FORNAME(idamax) (const FINT *N, const double *DX, const FINT *INCX); extern "C" FINT FORNAME(idamax) (const FINT *N, const double *DX, const FINT *INCX);
CBLAS_INDEX cblas_idamax(const int N, const double *X, const int incX) { CBLAS_INDEX cblas_idamax(const int N, const double *X, const int incX) {
#ifdef FORINT #ifdef FORINT
@@ -713,6 +1003,17 @@ CBLAS_INDEX cblas_izamax(const int N, const void *X, const int incX) {
#endif #endif
} }
extern "C" FINT FORNAME(icamax) (const FINT *N, const void *DX, const FINT *INCX);
CBLAS_INDEX cblas_icamax(const int N, const void *X, const int incX) {
#ifdef FORINT
const FINT ntmp=N;
const FINT incxtmp=incX;
return (CBLAS_INDEX)FORNAME(icamax)(&ntmp, X, &incxtmp)-1;
#else
return (CBLAS_INDEX)FORNAME(icamax)(&N, X, &incX)-1;
#endif
}
extern "C" FINT FORNAME(isamax) (const FINT *N, const float *DX, const FINT *INCX); extern "C" FINT FORNAME(isamax) (const FINT *N, const float *DX, const FINT *INCX);
CBLAS_INDEX cblas_isamax(const int N, const float *X, const int incX) { CBLAS_INDEX cblas_isamax(const int N, const float *X, const int incX) {
#ifdef FORINT #ifdef FORINT
@@ -788,7 +1089,6 @@ int clapack_sgesv(const CBLAS_ORDER Order, const int N, const int NRHS,
float *A, const int lda, int *ipiv, float *A, const int lda, int *ipiv,
float *B, const int ldb) float *B, const int ldb)
{ {
std::cout <<"In my clapack_sgesv\n";
FINT INFO=0; FINT INFO=0;
if(Order!=CblasRowMajor) laerror("CblasRowMajor order asserted"); if(Order!=CblasRowMajor) laerror("CblasRowMajor order asserted");
//B should be in the same physical order, just transpose A in place and the LU result on output //B should be in the same physical order, just transpose A in place and the LU result on output
@@ -848,4 +1148,16 @@ void cblas_zswap(const int N, void *X, const int incX, void *Y, const int incY){
#endif #endif
} }
extern "C" void FORNAME(cswap)(const FINT *N, void *X, const FINT *incX, void *Y, const FINT *incY);
void cblas_cswap(const int N, void *X, const int incX, void *Y, const int incY){
#ifdef FORINT
const FINT N_tmp = N;
const FINT incX_tmp = incX;
const FINT incY_tmp = incY;
FORNAME(cswap)(&N_tmp, X, &incX_tmp, Y, &incY_tmp);
#else
FORNAME(cswap)(&N, X, &incX, Y, &incY);
#endif
}
#endif #endif
+3
View File
@@ -689,5 +689,8 @@ int cblas_errprn(int ierr, int info, char *form, ...);
int clapack_dgesv(const CBLAS_ORDER Order, const int N, const int NRHS, int clapack_dgesv(const CBLAS_ORDER Order, const int N, const int NRHS,
double *A, const int lda, int *ipiv, double *A, const int lda, int *ipiv,
double *B, const int ldb); double *B, const int ldb);
int clapack_sgesv(const CBLAS_ORDER Order, const int N, const int NRHS,
float *A, const int lda, int *ipiv,
float *B, const int ldb);
#endif #endif
#endif #endif
+1825 -28
View File
File diff suppressed because it is too large. Load diff
+64 -19
View File
@@ -164,16 +164,18 @@ extern void singular_decomposition(NRMat<T> &a, NRMat<T> *u, NRVec<LA_traits<T>:
declare_la(double) declare_la(double)
declare_la(std::complex<double>) declare_la(std::complex<double>)
declare_la(float)
declare_la(std::complex<float>)
//svd_solve without contamination from nullspace, b is n x nrhs, result returns in b, A is destroyed //svd_solve without contamination from nullspace, b is n x nrhs, result returns in b, A is destroyed
template<typename T> template<typename T>
NRVec<T> svd_solve(NRMat<T> &a, const NRVec<T> &b, double thres=1e-12, int *nullity = NULL) NRVec<T> svd_solve(NRMat<T> &a, const NRVec<T> &b, typename LA_traits<T>::normtype thres=LA_precision<typename LA_traits<T>::normtype >::svd_threshold(), int *nullity = NULL)
{ {
if(b.size()!=a.nrows()) laerror("size mismatch in svd_solve"); if(b.size()!=a.nrows()) laerror("size mismatch in svd_solve");
if(nullity) *nullity=0; if(nullity) *nullity=0;
NRVec<double> w(a.ncols()); NRVec<typename LA_traits<T>::normtype > w(a.ncols());
NRMat<T> u(a.nrows(),a.ncols()); NRMat<T> u(a.nrows(),a.ncols());
NRMat<T> vt(a.ncols(),a.ncols()); NRMat<T> vt(a.ncols(),a.ncols());
singular_decomposition(a,&u,w,&vt,false); singular_decomposition(a,&u,w,&vt,false);
@@ -191,11 +193,11 @@ return res;
template<typename T> template<typename T>
NRMat<T> svd_solve(NRMat<T> &a, const NRMat<T> &b, double thres=1e-12, int *nullity = NULL) NRMat<T> svd_solve(NRMat<T> &a, const NRMat<T> &b, typename LA_traits<T>::normtype thres=LA_precision<typename LA_traits<T>::normtype >::svd_threshold(), int *nullity = NULL)
{ {
if(b.nrows()!=a.nrows()) laerror("size mismatch in svd_solve"); if(b.nrows()!=a.nrows()) laerror("size mismatch in svd_solve");
if(nullity) *nullity=0; if(nullity) *nullity=0;
NRVec<double> w(a.ncols()); NRVec<typename LA_traits<T>::normtype > w(a.ncols());
NRMat<T> u(a.nrows(),a.ncols()); NRMat<T> u(a.nrows(),a.ncols());
NRMat<T> vt(a.ncols(),a.ncols()); NRMat<T> vt(a.ncols(),a.ncols());
singular_decomposition(a,&u,w,&vt,false); singular_decomposition(a,&u,w,&vt,false);
@@ -221,10 +223,13 @@ return res;
extern void gdiagonalize(NRMat<double> &a, NRVec<double> &wr, NRVec<double> &wi, extern void gdiagonalize(NRMat<double> &a, NRVec<double> &wr, NRVec<double> &wi,
NRMat<double> *vl=NULL, NRMat<double> *vr=NULL, const bool corder=1, int n=0, const int sorttype=0, const int biorthonormalize=0, NRMat<double> *vl=NULL, NRMat<double> *vr=NULL, const bool corder=1, int n=0, const int sorttype=0, const int biorthonormalize=0,
NRMat<double> *b=NULL, NRVec<double> *beta=NULL); //this used real storage of eigenvectors like dgeev: for a complex pair (i,i+1) the eigenvector of w_i is Re + i Im = vector i + i*vector i+1 NRMat<double> *b=NULL, NRVec<double> *beta=NULL); //this used real storage of eigenvectors like dgeev: for a complex pair (i,i+1) the eigenvector of w_i is Re + i Im = vector i + i*vector i+1
extern void gdiagonalize(NRMat<float> &a, NRVec<float> &wr, NRVec<float> &wi,
NRMat<float> *vl=NULL, NRMat<float> *vr=NULL, const bool corder=1, int n=0, const int sorttype=0, const int biorthonormalize=0,
NRMat<float> *b=NULL, NRVec<float> *beta=NULL);
template<typename T> template<typename T>
extern void gdiagonalize(NRMat<T> &a, NRVec< std::complex<double> > &w, extern void gdiagonalize(NRMat<T> &a, NRVec<typename LA_traits<T>::complextype > &w,
NRMat< std::complex<double> >*vl=NULL, NRMat< std::complex<double> > *vr=NULL, NRMat<typename LA_traits<T>::complextype > *vl=NULL, NRMat<typename LA_traits<T>::complextype > *vr=NULL,
const bool corder=1, int n=0, const int sorttype=0, const int biorthonormalize=0, const bool corder=1, int n=0, const int sorttype=0, const int biorthonormalize=0,
NRMat<T> *b=NULL, NRVec<T> *beta=NULL); //eigenvectors are stored in complex matrices for T both double and complex NRMat<T> *b=NULL, NRVec<T> *beta=NULL); //eigenvectors are stored in complex matrices for T both double and complex
@@ -232,9 +237,13 @@ extern void gdiagonalize(NRMat<T> &a, NRVec< std::complex<double> > &w,
extern void gdiagonalize(NRMat<std::complex<double> > &a, NRVec<double> &wr, NRVec<double> &wi, extern void gdiagonalize(NRMat<std::complex<double> > &a, NRVec<double> &wr, NRVec<double> &wi,
NRMat<std::complex<double> > *vl=NULL, NRMat<std::complex<double> > *vr=NULL, const bool corder=1, int n=0, const int sorttype=0, const int biorthonormalize=0, NRMat<std::complex<double> > *vl=NULL, NRMat<std::complex<double> > *vr=NULL, const bool corder=1, int n=0, const int sorttype=0, const int biorthonormalize=0,
NRMat<std::complex<double> > *b=NULL, NRVec<std::complex<double> > *beta=NULL); NRMat<std::complex<double> > *b=NULL, NRVec<std::complex<double> > *beta=NULL);
extern void gdiagonalize(NRMat<std::complex<float> > &a, NRVec<float> &wr, NRVec<float> &wi,
NRMat<std::complex<float> > *vl=NULL, NRMat<std::complex<float> > *vr=NULL, const bool corder=1, int n=0, const int sorttype=0, const int biorthonormalize=0,
NRMat<std::complex<float> > *b=NULL, NRVec<std::complex<float> > *beta=NULL);
//diagonalization of symmetric real tridiagonal matrix //diagonalization of symmetric real tridiagonal matrix
extern void diagonalize3(NRVec<double> &d, NRVec<double> &d1, NRMat<double> *v, const bool corder=1, int n0=0); extern void diagonalize3(NRVec<double> &d, NRVec<double> &d1, NRMat<double> *v, const bool corder=1, int n0=0);
extern void diagonalize3(NRVec<float> &d, NRVec<float> &d1, NRMat<float> *v, const bool corder=1, int n0=0);
//complex,real,imaginary parts of various entities //complex,real,imaginary parts of various entities
template<typename T> template<typename T>
@@ -251,6 +260,8 @@ extern const typename LA_traits<T>::complextype complexmatrix (const T&, const T
//Cholesky decomposition //Cholesky decomposition
extern void cholesky(NRMat<double> &a, bool upper=1); extern void cholesky(NRMat<double> &a, bool upper=1);
extern void cholesky(NRMat<std::complex<double> > &a, bool upper=1); extern void cholesky(NRMat<std::complex<double> > &a, bool upper=1);
extern void cholesky(NRMat<float> &a, bool upper=1);
extern void cholesky(NRMat<std::complex<float> > &a, bool upper=1);
//inverse by means of linear solve, pass by value to preserve argument intact //inverse by means of linear solve, pass by value to preserve argument intact
template<typename T> template<typename T>
@@ -268,13 +279,13 @@ const NRMat<T> calcinverse(NRMat<T> a, T *det=NULL)
//inverse by means of SVD , pass by value to preserve argument intact //inverse by means of SVD , pass by value to preserve argument intact
template<typename T> template<typename T>
const NRMat<T> calcsvdinverse(NRMat<T> a, double thr=0, bool do_absadjoint=false) const NRMat<T> calcsvdinverse(NRMat<T> a, typename LA_traits<T>::normtype thr=0, bool do_absadjoint=false)
{ {
if(a.nrows()!=a.ncols()) laerror("svdinverse() for non-square matrix"); if(a.nrows()!=a.ncols()) laerror("svdinverse() for non-square matrix");
int n=a.nrows(); int n=a.nrows();
a.copyonwrite(); a.copyonwrite();
NRMat<T> u(n,n),v(n,n); NRMat<T> u(n,n),v(n,n);
NRVec<double> w(n),s(n); NRVec<typename LA_traits<T>::normtype > w(n),s(n);
singular_decomposition(a,&u,w,&v,true); singular_decomposition(a,&u,w,&v,true);
if(do_absadjoint) //absadjoint matrix - missing det(U)*det(V) to be full adjoint - a sign or complex phase if(do_absadjoint) //absadjoint matrix - missing det(U)*det(V) to be full adjoint - a sign or complex phase
{ {
@@ -348,7 +359,7 @@ return determinant_destroy(a);
// the info parameter of gesvx is returned (see man dgesvx) // the info parameter of gesvx is returned (see man dgesvx)
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
template<class T> template<class T>
int linear_solve_x(NRMat<T> &A, T *B, const int rhsCount, const int eqCount, const bool eq, const bool saveA, double *rcond); int linear_solve_x(NRMat<T> &A, T *B, const int rhsCount, const int eqCount, const bool eq, const bool saveA, typename LA_traits<T>::normtype *rcond);
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
@@ -364,7 +375,7 @@ int linear_solve_x(NRMat<T> &A, T *B, const int rhsCount, const int eqCount, con
// the info parameter of dgesvx is returned (see man dgesvx) // the info parameter of dgesvx is returned (see man dgesvx)
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
template<class T> template<class T>
int multiply_by_inverse(NRMat<T> &A, NRMat<T> &B, bool useEq=false, double *rcond=NULL); int multiply_by_inverse(NRMat<T> &A, NRMat<T> &B, bool useEq=false, typename LA_traits<T>::normtype *rcond=NULL);
//general submatrix, INDEX will typically be NRVec<int> or even int* //general submatrix, INDEX will typically be NRVec<int> or even int*
@@ -414,6 +425,7 @@ return r;
//auxiliary routine to adjust eigenvectors to guarantee real logarithm //auxiliary routine to adjust eigenvectors to guarantee real logarithm
extern void adjustphases(NRMat<double> &v); extern void adjustphases(NRMat<double> &v);
extern void adjustphases(NRMat<float> &v);
//declaration of template interface to cblas routines with full options available //declaration of template interface to cblas routines with full options available
@@ -443,6 +455,24 @@ inline double xdot<double>(int n, const double *x, int incx, const double *y, in
return cblas_ddot(n,x,incx,y,incy); return cblas_ddot(n,x,incx,y,incy);
} }
template<>
inline void xcopy<float> (int n, const float *x, int incx, float *y, int incy)
{
cblas_scopy(n, x, incx, y, incy);
}
template<>
inline void xaxpy<float>(int n, const float &a, const float *x, int incx, float *y, int incy)
{
cblas_saxpy(n, a, x, incx, y, incy);
}
template<>
inline float xdot<float>(int n, const float *x, int incx, const float *y, int incy)
{
return cblas_sdot(n,x,incx,y,incy);
}
//debugging aid: reconstruct an explicit matrix from the implicit version //debugging aid: reconstruct an explicit matrix from the implicit version
@@ -468,12 +498,14 @@ return r;
extern NRMat<double> realmatrixfunction(NRMat<double> a, double (*f) (double)); //a has to by in fact symmetric extern NRMat<double> realmatrixfunction(NRMat<double> a, double (*f) (double)); //a has to by in fact symmetric
extern NRMat<std::complex<double> > complexmatrixfunction(NRMat<double> a, double (*fre) (double), double (*fim) (double)); //a has to by in fact symmetric extern NRMat<std::complex<double> > complexmatrixfunction(NRMat<double> a, double (*fre) (double), double (*fim) (double)); //a has to by in fact symmetric
extern NRMat<float> realmatrixfunction(NRMat<float> a, float (*f) (float)); //a has to by in fact symmetric
extern NRMat<std::complex<float> > complexmatrixfunction(NRMat<float> a, float (*fre) (float), float (*fim) (float)); //a has to by in fact symmetric
template<typename T> template<typename T>
NRMat<T> matrixfunction(NRSMat<T> a, double (*f) (double)) //of symmetric/hermitian matrix NRMat<T> matrixfunction(NRSMat<T> a, typename LA_traits<T>::normtype (*f) (typename LA_traits<T>::normtype)) //of symmetric/hermitian matrix
{ {
int n = a.nrows(); int n = a.nrows();
NRVec<double> w(n); NRVec<typename LA_traits<T>::normtype > w(n);
NRMat<T> v(n, n); NRMat<T> v(n, n);
diagonalize(a, w, &v, 0); diagonalize(a, w, &v, 0);
@@ -488,18 +520,18 @@ NRMat<T> matrixfunction(NRSMat<T> a, double (*f) (double)) //of symmetric/hermit
template<typename T> template<typename T>
extern NRMat<T> matrixfunction(NRMat<T> a, std::complex<double> (*f)(const std::complex<double> &)) //of a general real/complex matrix extern NRMat<T> matrixfunction(NRMat<T> a, typename LA_traits<T>::complextype (*f)(const typename LA_traits<T>::complextype &)) //of a general real/complex matrix
{ {
int n = a.nrows(); int n = a.nrows();
NRVec<std::complex<double> > w(n); NRVec<typename LA_traits<T>::complextype > w(n);
NRMat<std::complex<double> > u(n,n),v(n,n); NRMat<typename LA_traits<T>::complextype > u(n,n),v(n,n);
#ifdef debugmf #ifdef debugmf
NRMat<std::complex<double> > a0=a; NRMat<typename LA_traits<T>::complextype > a0=a;
#endif #endif
gdiagonalize<T>(a, w, &u, &v, false,n,0,false,NULL,NULL);//a gets destroyed, eigenvectors are rows gdiagonalize<T>(a, w, &u, &v, false,n,0,false,NULL,NULL);//a gets destroyed, eigenvectors are rows
NRVec< std::complex<double> > z = diagofproduct(u, v, 1, 1); NRVec< typename LA_traits<T>::complextype > z = diagofproduct(u, v, 1, 1);
#ifdef debugmf #ifdef debugmf
std::cout <<"TEST matrixfunction\n"<<w<<u<<v<<z; std::cout <<"TEST matrixfunction\n"<<w<<u<<v<<z;
@@ -508,7 +540,7 @@ std::cout <<"TEST matrixfunction2 "<< a0*v.transpose(1) - v.transpose(1)*diagona
std::cout <<"TEST matrixfunction3 "<< u*v.transpose(1)<<diagonalmatrix(z)<<std::endl; std::cout <<"TEST matrixfunction3 "<< u*v.transpose(1)<<diagonalmatrix(z)<<std::endl;
#endif #endif
NRVec< std::complex<double> > wz(n); NRVec< typename LA_traits<T>::complextype > wz(n);
for (int i=0; i<a.nrows(); i++) wz[i] = w[i]/z[i]; for (int i=0; i<a.nrows(); i++) wz[i] = w[i]/z[i];
#ifdef debugmf #ifdef debugmf
@@ -518,7 +550,7 @@ std::cout <<"TEST matrixfunction4 "<< a0<< v.transpose(true)*diagonalmatrix(wz)*
for (int i=0; i<a.nrows(); i++) w[i] = (*f)(w[i])/z[i]; for (int i=0; i<a.nrows(); i++) w[i] = (*f)(w[i])/z[i];
u.diagmultl(w); u.diagmultl(w);
NRMat< std::complex<double> > r(n, n); NRMat< typename LA_traits<T>::complextype > r(n, n);
r.gemm(0.0, v, 'c', u, 'n', 1.0); r.gemm(0.0, v, 'c', u, 'n', 1.0);
return (NRMat<T>) r; //convert back to real if applicable by the explicit decomplexifying constructor; it is NOT checked to which accuracy the imaginary part is actually zero return (NRMat<T>) r; //convert back to real if applicable by the explicit decomplexifying constructor; it is NOT checked to which accuracy the imaginary part is actually zero
} }
@@ -528,6 +560,8 @@ std::cout <<"TEST matrixfunction4 "<< a0<< v.transpose(true)*diagonalmatrix(wz)*
extern std::complex<double> sqrtinv(const std::complex<double> &); extern std::complex<double> sqrtinv(const std::complex<double> &);
extern double sqrtinv(const double); extern double sqrtinv(const double);
extern std::complex<float> sqrtinv(const std::complex<float> &);
extern float sqrtinv(const float);
//functions on matrices //functions on matrices
inline NRMat<double> sqrt(const NRSMat<double> &a) { return matrixfunction(a,&std::sqrt); } inline NRMat<double> sqrt(const NRSMat<double> &a) { return matrixfunction(a,&std::sqrt); }
@@ -541,6 +575,17 @@ extern NRMat<std::complex<double> > exp0(const NRMat<std::complex<double> > &a);
extern NRMat<std::complex<double> > copytest(const NRMat<std::complex<double> > &a); extern NRMat<std::complex<double> > copytest(const NRMat<std::complex<double> > &a);
extern NRMat<double> copytest(const NRMat<double> &a); extern NRMat<double> copytest(const NRMat<double> &a);
extern NRMat<double> exp0(const NRMat<double> &a); extern NRMat<double> exp0(const NRMat<double> &a);
inline NRMat<float> sqrt(const NRSMat<float> &a) { return matrixfunction(a,&std::sqrt); }
inline NRMat<float> sqrtinv(const NRSMat<float> &a) { return matrixfunction(a,&sqrtinv); }
inline NRMat<float> realsqrt(const NRMat<float> &a) { return realmatrixfunction(a,&std::sqrt); }
inline NRMat<float> realsqrtinv(const NRMat<float> &a) { return realmatrixfunction(a,&sqrtinv); }
inline NRMat<float> log(const NRSMat<float> &a) { return matrixfunction(a,&std::log); }
extern NRMat<float> log(const NRMat<float> &a);
extern NRMat<std::complex<float> > log(const NRMat<std::complex<float> > &a);
extern NRMat<std::complex<float> > exp0(const NRMat<std::complex<float> > &a);
extern NRMat<std::complex<float> > copytest(const NRMat<std::complex<float> > &a);
extern NRMat<float> copytest(const NRMat<float> &a);
extern NRMat<float> exp0(const NRMat<float> &a);
}//namespace }//namespace
+8 -2
View File
@@ -1124,7 +1124,7 @@ NRSMat<T> A=J.transposedtimes(); //J^T J
NRVec<T> g=r*J; //J^T r, the gradient of f NRVec<T> g=r*J; //J^T r, the gradient of f
NRVec<T> diag(n); NRVec<T> diag(n);
for(int i=0; i<n; ++i) diag[i]=((const NRSMat<T> &)A)(i,i); for(int i=0; i<n; ++i) diag[i]=((const NRSMat<T> &)A)(i,i);
T mu=hp.my_tau*diag.amax(), nu=2; T mu=hp.my_scaling ? hp.my_tau : hp.my_tau*diag.amax(), nu=2;
for(; budget; ++st.iter) for(; budget; ++st.iter)
{ {
@@ -1163,7 +1163,12 @@ for(; budget; ++st.iter)
{ {
mu*=nu; nu*=2; mu*=nu; nu*=2;
const T eps=std::numeric_limits<T>::epsilon(); const T eps=std::numeric_limits<T>::epsilon();
if(h.norm()<=10*eps*(x.norm()+eps)) //no further improvement at machine precision (cf. Madsen, Nielsen & Tingleff eq. 3.16) //no further improvement at machine precision (cf. Madsen, Nielsen & Tingleff eq. 3.16), tested per component to be scale invariant:
//h_i^2 <= (10 eps)^2 (x_i^2+eps^2) for all i (with |h|<=10 eps |x| badly scaled parameters stopped too early in float)
NRVec<T> hh(h); hh*=h;
NRVec<T> xx(x); xx*=x; xx+=eps*eps;
hh/=xx;
if(hh.amax()<=100*eps*eps)
{ {
if(verbose) std::cout <<name<<": step at machine precision\n"; if(verbose) std::cout <<name<<": step at machine precision\n";
st.converged=true; st.converged=true;
@@ -2080,6 +2085,7 @@ template optresult<T> firstorder_minimize(const char *name, firstorder_step<T> &
template optresult<T> trustregion_minimize(const char *name, trustregion_step<T> &stepper, const OptProblem<T> *o, NRVec<T> &x, const trustregion_hyperparameters<T> &hp, hyperparameters<T> *achieved); \ template optresult<T> trustregion_minimize(const char *name, trustregion_step<T> &stepper, const OptProblem<T> *o, NRVec<T> &x, const trustregion_hyperparameters<T> &hp, hyperparameters<T> *achieved); \
INSTANTIZE(double) INSTANTIZE(double)
INSTANTIZE(float)
}//namespace }//namespace
+43 -12
View File
@@ -1,6 +1,6 @@
/* /*
LA: linear algebra C++ interface library LA: linear algebra C++ interface library
Copyright (C) 2021 Jiri Pittner <jiri.pittner@jh-inst.cas.cz> or <jiri@pittnerovi.com> Copyright (C) 2026 Jiri Pittner <jiri.pittner@jh-inst.cas.cz> or <jiri@pittnerovi.com>
This code has been prepared with the assistance of an AI (Claude Opus 5.5, Anthropic). This code has been prepared with the assistance of an AI (Claude Opus 5.5, Anthropic).
This program is free software: you can redistribute it and/or modify This program is free software: you can redistribute it and/or modify
@@ -34,6 +34,37 @@
namespace LA { namespace LA {
//precision-dependent default steps and thresholds of the optimizers; only double and float are supported (the double values are those used so far)
template<typename T> struct opt_precision;
template<> struct opt_precision<double>
{
static double fd_central_step() {return 1e-6;} //central differences (FiniteDifference, ParameterShift), optimum ~ eps^(1/3)
static double fd_forward_step() {return 1e-7;} //forward differences (OptLeastSquares, LM), optimum ~ eps^(1/2)
static double g_thr() {return 1e-6;} //gradient convergence
static double sy_thr() {return 1e-12;} //curvature condition of the quasi-Newton updates
static double sr1_thr() {return 1e-8;} //SR1 skipping rule
static double update_thr() {return 1e-8;} //trust-region Hessian update skipping rule
static double nm_f_thr() {return 1e-8;} //Nelder-Mead stopping
static double nm_x_thr() {return 1e-6;}
static double powell_f_thr() {return 1e-10;} //Powell stopping
static double powell_x_thr() {return 1e-6;}
static double rhoend() {return 1e-6;} //final COBYLA trust radius
};
template<> struct opt_precision<float>
{
static float fd_central_step() {return 5e-3f;}
static float fd_forward_step() {return 3e-4f;}
static float g_thr() {return 1e-3f;}
static float sy_thr() {return 1e-6f;}
static float sr1_thr() {return 1e-4f;}
static float update_thr() {return 1e-4f;}
static float nm_f_thr() {return 1e-5f;}
static float nm_x_thr() {return 1e-4f;}
static float powell_f_thr() {return 1e-6f;}
static float powell_x_thr() {return 1e-4f;}
static float rhoend() {return 1e-4f;}
};
//base classes with purely virtual functions for multiple inheritance use //base classes with purely virtual functions for multiple inheritance use
//optimization problem - provides function and optional gradient, in multiply inherited classess will store auxiliary data for the function //optimization problem - provides function and optional gradient, in multiply inherited classess will store auxiliary data for the function
@@ -102,7 +133,7 @@ template<typename T>
class OptLeastSquares : public OptProblem<T>, public OptResiduals<T> { class OptLeastSquares : public OptProblem<T>, public OptResiduals<T> {
public: public:
T fd_step; //relative step of the forward-difference Jacobian T fd_step; //relative step of the forward-difference Jacobian
OptLeastSquares(const T h=1e-7) : fd_step(h) {}; OptLeastSquares(const T h=opt_precision<T>::fd_forward_step()) : fd_step(h) {};
void jacobian_fd(const NRVec<T> &x, const NRVec<T> &r, NRMat<T> &J) const; //Jacobian by forward differences, r = residuals at x void jacobian_fd(const NRVec<T> &x, const NRVec<T> &r, NRMat<T> &J) const; //Jacobian by forward differences, r = residuals at x
virtual T funcgrad(const NRVec<T> &x, NRVec<T> *grad) const; virtual T funcgrad(const NRVec<T> &x, NRVec<T> *grad) const;
virtual ~OptLeastSquares() {}; virtual ~OptLeastSquares() {};
@@ -164,7 +195,7 @@ class ParameterShift : public OptProblemAdapter<T> {
public: public:
const OptTrigonometric<T> *trig; const OptTrigonometric<T> *trig;
T h; T h;
ParameterShift(const OptProblem<T> *pp, const T hh=1e-6); ParameterShift(const OptProblem<T> *pp, const T hh=opt_precision<T>::fd_central_step());
virtual T funcgrad(const NRVec<T> &x, NRVec<T> *grad) const; virtual T funcgrad(const NRVec<T> &x, NRVec<T> *grad) const;
}; };
@@ -173,7 +204,7 @@ template<typename T>
class FiniteDifference : public OptProblemAdapter<T> { class FiniteDifference : public OptProblemAdapter<T> {
public: public:
T h; T h;
FiniteDifference(const OptProblem<T> *pp, const T hh=1e-6) : OptProblemAdapter<T>(pp), h(hh) {}; FiniteDifference(const OptProblem<T> *pp, const T hh=opt_precision<T>::fd_central_step()) : OptProblemAdapter<T>(pp), h(hh) {};
virtual T funcgrad(const NRVec<T> &x, NRVec<T> *grad) const; virtual T funcgrad(const NRVec<T> &x, NRVec<T> *grad) const;
}; };
@@ -207,7 +238,7 @@ template<typename T>
class gradient_hyperparameters : public hyperparameters<T> { class gradient_hyperparameters : public hyperparameters<T> {
public: public:
T my_g_thr; //gradient norm convergence threshold T my_g_thr; //gradient norm convergence threshold
gradient_hyperparameters() : my_g_thr(1e-6) {}; gradient_hyperparameters() : my_g_thr(opt_precision<T>::g_thr()) {};
virtual void setfrom(const hyperparameters<T> &p) {const gradient_hyperparameters *q=dynamic_cast<const gradient_hyperparameters *>(&p); if(q) *this= *q; else hyperparameters<T>::setfrom(p);} virtual void setfrom(const hyperparameters<T> &p) {const gradient_hyperparameters *q=dynamic_cast<const gradient_hyperparameters *>(&p); if(q) *this= *q; else hyperparameters<T>::setfrom(p);}
virtual bool converged(const optstate<T> &s) const {return (s.gnorm>=0 && s.gnorm<my_g_thr) || hyperparameters<T>::converged(s);} virtual bool converged(const optstate<T> &s) const {return (s.gnorm>=0 && s.gnorm<my_g_thr) || hyperparameters<T>::converged(s);}
virtual void setreached(const optstate<T> &s) {hyperparameters<T>::setreached(s); my_g_thr=s.gnorm;} virtual void setreached(const optstate<T> &s) {hyperparameters<T>::setreached(s); my_g_thr=s.gnorm;}
@@ -379,7 +410,7 @@ template<typename T>
class quasinewton_hyperparameters : public linesearch_hyperparameters<T> { class quasinewton_hyperparameters : public linesearch_hyperparameters<T> {
public: public:
T my_sy_thr; //skip the BFGS-type update if s.y <= sy_thr*|s|*|y| (curvature condition violated numerically) T my_sy_thr; //skip the BFGS-type update if s.y <= sy_thr*|s|*|y| (curvature condition violated numerically)
quasinewton_hyperparameters() : my_sy_thr(1e-12) {}; quasinewton_hyperparameters() : my_sy_thr(opt_precision<T>::sy_thr()) {};
virtual void setfrom(const hyperparameters<T> &p) {const quasinewton_hyperparameters *q=dynamic_cast<const quasinewton_hyperparameters *>(&p); if(q) *this= *q; else linesearch_hyperparameters<T>::setfrom(p);} virtual void setfrom(const hyperparameters<T> &p) {const quasinewton_hyperparameters *q=dynamic_cast<const quasinewton_hyperparameters *>(&p); if(q) *this= *q; else linesearch_hyperparameters<T>::setfrom(p);}
}; };
@@ -392,7 +423,7 @@ class BFGS_hyperparameters : public quasinewton_hyperparameters<T> {
public: public:
QN_update my_update; //BFGS, DFP or SR1 (Nocedal & Wright eqs. 6.17, 6.15, 6.25); DFP needs an accurate line search (c2~0.1), otherwise it can stall QN_update my_update; //BFGS, DFP or SR1 (Nocedal & Wright eqs. 6.17, 6.15, 6.25); DFP needs an accurate line search (c2~0.1), otherwise it can stall
T my_sr1_thr; //SR1: skip the update if |(s-Hy).y| <= sr1_thr*|s-Hy|*|y| (cf. Nocedal & Wright eq. 6.26) T my_sr1_thr; //SR1: skip the update if |(s-Hy).y| <= sr1_thr*|s-Hy|*|y| (cf. Nocedal & Wright eq. 6.26)
BFGS_hyperparameters() : my_update(QN_BFGS), my_sr1_thr(1e-8) {}; BFGS_hyperparameters() : my_update(QN_BFGS), my_sr1_thr(opt_precision<T>::sr1_thr()) {};
virtual void setfrom(const hyperparameters<T> &p) {const BFGS_hyperparameters *q=dynamic_cast<const BFGS_hyperparameters *>(&p); if(q) *this= *q; else quasinewton_hyperparameters<T>::setfrom(p);} virtual void setfrom(const hyperparameters<T> &p) {const BFGS_hyperparameters *q=dynamic_cast<const BFGS_hyperparameters *>(&p); if(q) *this= *q; else quasinewton_hyperparameters<T>::setfrom(p);}
}; };
@@ -485,7 +516,7 @@ T my_eta; //a step of a minimization is accepted if (actual reduction)/(predicte
TR_hessian my_hessian; TR_hessian my_hessian;
int my_hessian_recompute; //exact Hessian computed every k-th iteration and updated in between; 1 = always exact int my_hessian_recompute; //exact Hessian computed every k-th iteration and updated in between; 1 = always exact
T my_update_thr; //threshold for skipping the Hessian updates (curvature condition of BFGS, denominator of SR1) T my_update_thr; //threshold for skipping the Hessian updates (curvature condition of BFGS, denominator of SR1)
trustregion_hyperparameters() : my_delta0(1), my_deltamax(1e3), my_eta(1e-4), my_hessian(TR_AUTO), my_hessian_recompute(1), my_update_thr(1e-8) {}; trustregion_hyperparameters() : my_delta0(1), my_deltamax(1e3), my_eta(1e-4), my_hessian(TR_AUTO), my_hessian_recompute(1), my_update_thr(opt_precision<T>::update_thr()) {};
virtual void setfrom(const hyperparameters<T> &p) {const trustregion_hyperparameters *q=dynamic_cast<const trustregion_hyperparameters *>(&p); if(q) *this= *q; else gradient_hyperparameters<T>::setfrom(p);} virtual void setfrom(const hyperparameters<T> &p) {const trustregion_hyperparameters *q=dynamic_cast<const trustregion_hyperparameters *>(&p); if(q) *this= *q; else gradient_hyperparameters<T>::setfrom(p);}
}; };
@@ -553,7 +584,7 @@ public:
T my_tau; //initial damping mu = tau*max diag(J^T J) T my_tau; //initial damping mu = tau*max diag(J^T J)
bool my_scaling; //Marquardt scaling D = diag(J^T J) bool my_scaling; //Marquardt scaling D = diag(J^T J)
T my_fd_step; //relative step of the forward-difference Jacobian T my_fd_step; //relative step of the forward-difference Jacobian
LM_hyperparameters() : my_tau(1e-3), my_scaling(false), my_fd_step(1e-7) {}; LM_hyperparameters() : my_tau(1e-3), my_scaling(false), my_fd_step(opt_precision<T>::fd_forward_step()) {};
virtual void setfrom(const hyperparameters<T> &p) {const LM_hyperparameters *q=dynamic_cast<const LM_hyperparameters *>(&p); if(q) *this= *q; else gradient_hyperparameters<T>::setfrom(p);} virtual void setfrom(const hyperparameters<T> &p) {const LM_hyperparameters *q=dynamic_cast<const LM_hyperparameters *>(&p); if(q) *this= *q; else gradient_hyperparameters<T>::setfrom(p);}
}; };
@@ -576,7 +607,7 @@ T my_alpha; //reflection
T my_gamma; //expansion T my_gamma; //expansion
T my_rho; //contraction T my_rho; //contraction
T my_sigma; //shrink T my_sigma; //shrink
NelderMead_hyperparameters() : my_initstep(0.1), my_adaptive(true), my_alpha(1), my_gamma(2), my_rho(0.5), my_sigma(0.5) {this->my_maxstep=5000; this->my_f_thr=1e-8; this->my_x_thr=1e-6;}; NelderMead_hyperparameters() : my_initstep(0.1), my_adaptive(true), my_alpha(1), my_gamma(2), my_rho(0.5), my_sigma(0.5) {this->my_maxstep=5000; this->my_f_thr=opt_precision<T>::nm_f_thr(); this->my_x_thr=opt_precision<T>::nm_x_thr();};
virtual void setfrom(const hyperparameters<T> &p) {const NelderMead_hyperparameters *q=dynamic_cast<const NelderMead_hyperparameters *>(&p); if(q) *this= *q; else hyperparameters<T>::setfrom(p);} virtual void setfrom(const hyperparameters<T> &p) {const NelderMead_hyperparameters *q=dynamic_cast<const NelderMead_hyperparameters *>(&p); if(q) *this= *q; else hyperparameters<T>::setfrom(p);}
}; };
@@ -596,7 +627,7 @@ public:
T my_initstep; //initial trial step of the line minimizations T my_initstep; //initial trial step of the line minimizations
T my_linmin_tol; //relative tolerance of the line minimizations, the absolute one is 0.01*x_thr T my_linmin_tol; //relative tolerance of the line minimizations, the absolute one is 0.01*x_thr
int my_maxlinemin; //max. no. of function evaluations in one line minimization int my_maxlinemin; //max. no. of function evaluations in one line minimization
Powell_hyperparameters() : my_initstep(0.1), my_linmin_tol(1e-4), my_maxlinemin(50) {this->my_maxstep=1000; this->my_f_thr=1e-10; this->my_x_thr=1e-6;}; Powell_hyperparameters() : my_initstep(0.1), my_linmin_tol(1e-4), my_maxlinemin(50) {this->my_maxstep=1000; this->my_f_thr=opt_precision<T>::powell_f_thr(); this->my_x_thr=opt_precision<T>::powell_x_thr();};
virtual void setfrom(const hyperparameters<T> &p) {const Powell_hyperparameters *q=dynamic_cast<const Powell_hyperparameters *>(&p); if(q) *this= *q; else hyperparameters<T>::setfrom(p);} virtual void setfrom(const hyperparameters<T> &p) {const Powell_hyperparameters *q=dynamic_cast<const Powell_hyperparameters *>(&p); if(q) *this= *q; else hyperparameters<T>::setfrom(p);}
}; };
@@ -620,7 +651,7 @@ T my_rhobeg; //initial trust region radius, about one tenth of the expected chan
T my_rhoend; //final trust region radius, the accuracy of x T my_rhoend; //final trust region radius, the accuracy of x
bool my_flexible_radius; //trust region radius delta >= rho revised by the reduction ratio and rho reduced by the rules of NEWUOA bool my_flexible_radius; //trust region radius delta >= rho revised by the reduction ratio and rho reduced by the rules of NEWUOA
//(Powell, Report DAMTP 2004/NA08, eqs. (7.1), (7.6), (6.16)), so that delta can also increase; false = Powell's original COBYLA with delta = rho //(Powell, Report DAMTP 2004/NA08, eqs. (7.1), (7.6), (6.16)), so that delta can also increase; false = Powell's original COBYLA with delta = rho
Cobyla_hyperparameters() : my_rhobeg(0.5), my_rhoend(1e-6), my_flexible_radius(true) {this->my_maxstep=100000;}; Cobyla_hyperparameters() : my_rhobeg(0.5), my_rhoend(opt_precision<T>::rhoend()), my_flexible_radius(true) {this->my_maxstep=100000;};
virtual void setfrom(const hyperparameters<T> &p) {const Cobyla_hyperparameters *q=dynamic_cast<const Cobyla_hyperparameters *>(&p); if(q) *this= *q; else hyperparameters<T>::setfrom(p);} virtual void setfrom(const hyperparameters<T> &p) {const Cobyla_hyperparameters *q=dynamic_cast<const Cobyla_hyperparameters *>(&p); if(q) *this= *q; else hyperparameters<T>::setfrom(p);}
}; };
+5
View File
@@ -264,6 +264,8 @@ template class Polynomial<int>;
template class Polynomial<unsigned int>; template class Polynomial<unsigned int>;
template class Polynomial<double>; template class Polynomial<double>;
template class Polynomial<std::complex<double> >; template class Polynomial<std::complex<double> >;
template class Polynomial<float>;
template class Polynomial<std::complex<float> >;
#define INSTANTIZE(T) \ #define INSTANTIZE(T) \
template NRMat<T> Sylvester(const Polynomial<T> &p, const Polynomial<T> &q); \ template NRMat<T> Sylvester(const Polynomial<T> &p, const Polynomial<T> &q); \
@@ -277,12 +279,15 @@ INSTANTIZE(int)
INSTANTIZE(unsigned int) INSTANTIZE(unsigned int)
INSTANTIZE(double) INSTANTIZE(double)
INSTANTIZE(std::complex<double>) INSTANTIZE(std::complex<double>)
INSTANTIZE(float)
INSTANTIZE(std::complex<float>)
#define INSTANTIZE2(T) \ #define INSTANTIZE2(T) \
template Polynomial<T> hermite_polynomial(int); \ template Polynomial<T> hermite_polynomial(int); \
INSTANTIZE2(int) INSTANTIZE2(int)
INSTANTIZE2(double) INSTANTIZE2(double)
INSTANTIZE2(float)
+3 -3
View File
@@ -86,7 +86,7 @@ public:
while(n>0 && MYABS((*this)[n])<=thr) --n; while(n>0 && MYABS((*this)[n])<=thr) --n;
resize(n,true); resize(n,true);
}; };
void normalize() {if((*this)[degree()]==(T)0) laerror("zero coefficient at highest degree - simplify first"); *this /= (*this)[degree()];}; void normalize() {const T lead=((const Polynomial &)*this)[degree()]; if(lead==(T)0) laerror("zero coefficient at highest degree - simplify first"); *this /= lead;};
Polynomial shifted(const int shift) const Polynomial shifted(const int shift) const
{ {
if(shift==0) return *this; if(shift==0) return *this;
@@ -154,9 +154,9 @@ public:
Polynomial operator/(const Polynomial &rhs) const {Polynomial q,r; polydiv(rhs,q,r); return q;}; Polynomial operator/(const Polynomial &rhs) const {Polynomial q,r; polydiv(rhs,q,r); return q;};
Polynomial operator%(const Polynomial &rhs) const {Polynomial q,r; polydiv(rhs,q,r); return r;}; Polynomial operator%(const Polynomial &rhs) const {Polynomial q,r; polydiv(rhs,q,r); return r;};
NRMat<T> companion() const; //matrix which has this characteristic polynomial NRMat<T> companion() const; //matrix which has this characteristic polynomial
NRVec<typename LA_traits<T>::complextype> roots() const; //implemented for complex<double> and double only NRVec<typename LA_traits<T>::complextype> roots() const; //implemented for double, float and their complex types only
NRVec<T> realroots(const typename LA_traits<T>::normtype thr) const; NRVec<T> realroots(const typename LA_traits<T>::normtype thr) const;
T newton(const T x0, const typename LA_traits<T>::normtype thr=1e-14, const int maxit=1000) const; //solve root from the guess T newton(const T x0, const typename LA_traits<T>::normtype thr=LA_precision<typename LA_traits<T>::normtype>::newton_threshold(), const int maxit=1000) const; //solve root from the guess
Polynomial pow(const int i) const; //integer power Polynomial pow(const int i) const; //integer power
Polynomial powx(const int i) const; //substitute x^i for x in the polynomial Polynomial powx(const int i) const; //substitute x^i for x in the polynomial
void binomial(const int n); //(1+x)^n void binomial(const int n); //(1+x)^n
+454 -3
View File
@@ -167,7 +167,7 @@ const T* NRSMat<T>::diagonalof(NRVec<T> &r, const bool divide, bool cache) const
if(divide){ if(divide){
for(register int i=0; i<nn; i++){ for(register int i=0; i<nn; i++){
const T a = v[(size_t)i*(i+1)/2+i]; const T a = v[(size_t)i*(i+1)/2+i];
if(a != 0.) r[i] /= a; if(a != (T)0) r[i] /= a;
} }
}else{ }else{
for(register int i=0; i<nn; i++) r[i] = v[(size_t)i*(i+1)/2+i]; for(register int i=0; i<nn; i++) r[i] = v[(size_t)i*(i+1)/2+i];
@@ -215,6 +215,26 @@ const NRSMat<double> NRSMat<double>::operator-() const {
return result; return result;
} }
template <>
const NRSMat<float> NRSMat<float>::operator-() const {
NRSMat<float> result(nn, getlocation());
#ifdef CUDALA
if(location == cpu){
#endif
memcpy(result.v, v, NN2*sizeof(float));
cblas_sscal(NN2, -1., result.v, 1);
#ifdef CUDALA
}else{
cublasScopy(NN2, v, 1, result.v, 1);
TEST_CUBLAS("cublasScopy");
cublasSscal(NN2, -1., result.v, 1);
TEST_CUBLAS("cublasSscal");
}
#endif
return result;
}
/***************************************************************************//** /***************************************************************************//**
* implements unary minus operator for this hermitian matrix * implements unary minus operator for this hermitian matrix
* @return modified copy of this matrix * @return modified copy of this matrix
@@ -240,6 +260,27 @@ const NRSMat<std::complex<double> > NRSMat<std::complex<double> >::operator-() c
return result; return result;
} }
template <>
const NRSMat<std::complex<float> > NRSMat<std::complex<float> >::operator-() const {
NRSMat<std::complex<float> > result(nn, getlocation());
#ifdef CUDALA
if(location == cpu) {
#endif
memcpy(result.v, v, NN2*sizeof(std::complex<float>));
cblas_cscal(NN2, &CMONEF, result.v, 1);
#ifdef CUDALA
}else{
cublasCcopy(NN2, (cuComplex*)v, 1, (cuComplex*)result.v, 1);
TEST_CUBLAS("cublasCcopy");
cublasCscal(NN2, CUMONE, (cuComplex*)result.v, 1);
TEST_CUBLAS("cublasCscal");
}
#endif
return result;
}
/***************************************************************************//** /***************************************************************************//**
* @return the sum of the diagonal elements * @return the sum of the diagonal elements
******************************************************************************/ ******************************************************************************/
@@ -266,6 +307,16 @@ void NRSMat<double>::randomize(const double &x) {
} }
} }
template<>
void NRSMat<float>::randomize(const float &x) {
NOT_GPU(*this);
copyonwrite();
for(size_t i=0; i<NN2; ++i){
v[i] = x*RANDDOUBLESIGNED();
}
}
/***************************************************************************//** /***************************************************************************//**
* Fill this hermitian matrix with pseudorandom numbers generated from uniform * Fill this hermitian matrix with pseudorandom numbers generated from uniform
* distribution. The real and imaginary parts are generated independently. * distribution. The real and imaginary parts are generated independently.
@@ -282,6 +333,18 @@ void NRSMat<std::complex<double> >::randomize(const double &x) {
} }
} }
template<>
void NRSMat<std::complex<float> >::randomize(const float &x) {
copyonwrite();
for(register size_t i=0; i<NN2; ++i) v[i].real(x*RANDDOUBLESIGNED());
for(register size_t i=0; i<NN2; ++i) v[i].imag(x*RANDDOUBLESIGNED());
for(register int i=0; i<nn; ++i){
for(register int j=0; j<=i; ++j){
if(i == j) v[i*(size_t)(i+1)/2+j].imag(0.); //hermitean
}
}
}
/***************************************************************************//** /***************************************************************************//**
* routine for formatted output via lawritemat * routine for formatted output via lawritemat
* @param[in] file pointer to <tt>FILE</tt> structure representing the output file * @param[in] file pointer to <tt>FILE</tt> structure representing the output file
@@ -390,6 +453,30 @@ const NRMat<double> NRSMat<double>::operator*(const NRMat<double> &rhs) const {
return result; return result;
} }
template<>
const NRMat<float> NRSMat<float>::operator*(const NRMat<float> &rhs) const {
#ifdef DEBUG
if(nn != rhs.nrows()) laerror("incompatible dimensions in NRMat<float> NRSMat<float>::operator*(const NRMat<float> &)");
#endif
SAME_LOC(*this, rhs);
NRMat<float> result(nn, rhs.ncols(), getlocation());
#ifdef CUDALA
if(location == cpu){
#endif
for(register int k = 0; k<rhs.ncols(); k++){
cblas_sspmv(CblasRowMajor, CblasLower, nn, 1.0, v, rhs[0] + k, rhs.ncols(), 0.0, result[0] + k, rhs.ncols());
}
#ifdef CUDALA
}else{
for(register int k = 0; k<rhs.ncols(); k++){
cublasSspmv('U', nn, 1.0, v, rhs[0] + k, rhs.ncols(), 0.0, result[0] + k, rhs.ncols());
TEST_CUBLAS("cublasSspmv");
}
}
#endif
return result;
}
/***************************************************************************//** /***************************************************************************//**
* multiply this real double-precision symmetric matrix \f$S\f$ stored in packed form * multiply this real double-precision symmetric matrix \f$S\f$ stored in packed form
@@ -425,6 +512,34 @@ NRSMat<std::complex<double> >::operator*(const NRMat<std::complex<double> > &rhs
return result; return result;
} }
template<>
const NRMat<std::complex<float> >
NRSMat<std::complex<float> >::operator*(const NRMat<std::complex<float> > &rhs) const {
#ifdef DEBUG
if (nn != rhs.nrows()) laerror("incompatible dimensions in NRSMat<std::complex<float> >::operator*(const NRMat<std::complex<float> > &)");
#endif
SAME_LOC(*this, rhs);
NRMat<std::complex<float> > result(nn, rhs.ncols(), getlocation());
#ifdef CUDALA
if(location == cpu){
#endif
for(register int k=0; k<rhs.ncols(); k++){
cblas_chpmv(CblasRowMajor, CblasLower, nn, &CONEF, v, rhs[0]+k, rhs.ncols(), &CZEROF, result[0]+k, rhs.ncols());
}
#ifdef CUDALA
}else{
for(register int k = 0; k<rhs.ncols(); k++){
cublasChpmv('U', nn,
CUONE, (cuComplex*)v, (cuComplex*)(rhs[0] + k), rhs.ncols(),
CUZERO, (cuComplex*)(result[0] + k), rhs.ncols());
TEST_CUBLAS("cublasSspmv");
}
}
#endif
return result;
}
/***************************************************************************//** /***************************************************************************//**
* multiply this real double-precision symmetric matrix \f$S\f$ stored in packed form * multiply this real double-precision symmetric matrix \f$S\f$ stored in packed form
* with real double-precision symmetric matrix \f$T\f$ * with real double-precision symmetric matrix \f$T\f$
@@ -478,6 +593,54 @@ const NRMat<double> NRSMat<double>::operator*(const NRSMat<double> &rhs) const {
return result; return result;
} }
template<>
const NRMat<float> NRSMat<float>::operator*(const NRSMat<float> &rhs) const {
#ifdef DEBUG
if (nn != rhs.nn) laerror("incompatible dimensions in NRMat<float> NRSMat<float>::operator*(const NRSMat<float> &)");
#endif
NRMat<float> result(0.0, nn, nn);
float *p, *q;
p = v;
for (int i=0; i<nn;i++) {
q = rhs.v;
for (int k=0; k<=i; k++) {
cblas_saxpy(k+1, *p++, q, 1, result[i], 1);
q += k+1;
}
}
p = v;
for (int i=0; i<nn;i++) {
q = rhs.v+1;
for (int j=1; j<nn; j++) {
result[i][j] += cblas_sdot(i+1<j ? i+1 : j, p, 1, q, 1);
q += j+1;
}
p += i+1;
}
p = v;
q = rhs.v;
for (int i=0; i<nn; i++) {
cblas_sger(CblasRowMajor, i, i+1, 1., p, 1, q, 1, result, nn);
p += i+1;
q += i+1;
}
q = rhs.v+3;
for (int j=2; j<nn; j++) {
p = v+1;
for (int i=1; i<j; i++) {
cblas_saxpy(i, *++q, p, 1, result[0]+j, nn);
p += i+1;
}
q += 2;
}
return result;
}
/***************************************************************************//** /***************************************************************************//**
* multiply this complex double-precision symmetric matrix \f$G\f$ stored in packed form * multiply this complex double-precision symmetric matrix \f$G\f$ stored in packed form
@@ -497,6 +660,19 @@ NRSMat<std::complex<double> >::operator*(const NRSMat<std::complex<double> > &rh
return result; return result;
} }
template<>
const NRMat<std::complex<float> >
NRSMat<std::complex<float> >::operator*(const NRSMat<std::complex<float> > &rhs) const {
#ifdef DEBUG
if (nn != rhs.nn) laerror("incompatible dimensions in NRSMat<std::complex<float> >::operator*(const NRSMat<std::complex<float> > &)");
#endif
SAME_LOC(*this, rhs);
NRMat<std::complex<float> > result(nn, nn, getlocation());
NRMat<std::complex<float> > rhsmat(rhs);
result = *this * rhsmat;
return result;
}
/***************************************************************************//** /***************************************************************************//**
* compute inner product of this real symmetric matrix \f$A\f$ with given real symmetric matrix \f$B\f$ * compute inner product of this real symmetric matrix \f$A\f$ with given real symmetric matrix \f$B\f$
@@ -524,6 +700,25 @@ const double NRSMat<double>::dot(const NRSMat<double> &rhs) const {
return ret; return ret;
} }
template<>
const float NRSMat<float>::dot(const NRSMat<float> &rhs) const {
float ret(0.);
#ifdef DEBUG
if (nn != rhs.nn) laerror("incompatible dimensions in float NRSMat<float>::dot(const NRSMat<float> &)");
#endif
SAME_LOC(*this, rhs);
#ifdef CUDALA
if(location == cpu){
#endif
ret = cblas_sdot(NN2, v, 1, rhs.v, 1);
#ifdef CUDALA
}else{
ret = cublasSdot(NN2, v, 1, rhs.v, 1);
}
#endif
return ret;
}
/***************************************************************************//** /***************************************************************************//**
* compute inner product of this complex symmetric matrix \f$A\f$ with given complex symmetric matrix \f$B\f$ * compute inner product of this complex symmetric matrix \f$A\f$ with given complex symmetric matrix \f$B\f$
@@ -554,6 +749,28 @@ const std::complex<double> NRSMat<std::complex<double> >::dot(const NRSMat<std::
return dot; return dot;
} }
template<>
const std::complex<float> NRSMat<std::complex<float> >::dot(const NRSMat<std::complex<float> > &rhs) const {
#ifdef DEBUG
if (nn != rhs.nn) laerror("incompatible dimensions in std::complex<float> NRSMat<std::complex<float> >::dot(const NRSMat<std::complex<float> > &)");
#endif
std::complex<float> dot(0., 0.);
SAME_LOC(*this, rhs);
#ifdef CUDALA
if(location == cpu){
#endif
cblas_cdotc_sub(NN2, v, 1, rhs.v, 1, &dot);
#ifdef CUDALA
}else{
const cuComplex _dot = cublasCdotc(NN2, (cuComplex*)v, 1, (cuComplex*)(rhs.v), 1);
dot = std::complex<float>(cuCreal(_dot), cuCimag(_dot));
TEST_CUBLAS("cublasCdotc");
}
#endif
return dot;
}
/***************************************************************************//** /***************************************************************************//**
* compute inner product of this real double-precision symmetric matrix \f$S\f$ of order \f$n\f$ * compute inner product of this real double-precision symmetric matrix \f$S\f$ of order \f$n\f$
@@ -581,6 +798,26 @@ const double NRSMat<double>::dot(const NRVec<double> &rhs) const {
return ret; return ret;
} }
template<>
const float NRSMat<float>::dot(const NRVec<float> &rhs) const {
float ret(0.0);
#ifdef DEBUG
if(NN2 != rhs.nn) laerror("incompatible dimensions in float NRSMat<float>::dot(const NRVec<float> &)");
#endif
SAME_LOC(*this, rhs);
#ifdef CUDALA
if(location == cpu){
#endif
ret = cblas_sdot(NN2, v, 1, rhs.v, 1);
#ifdef CUDALA
}else{
ret = cublasSdot(NN2, v, 1, rhs.v, 1);
TEST_CUBLAS("cublasSdot");
}
#endif
return ret;
}
/***************************************************************************//** /***************************************************************************//**
* compute inner product of this complex double-precision hermitian matrix \f$H\f$ of order \f$n\f$ * compute inner product of this complex double-precision hermitian matrix \f$H\f$ of order \f$n\f$
@@ -610,6 +847,28 @@ NRSMat<std::complex<double> >::dot(const NRVec<std::complex<double> > &rhs) cons
return dot; return dot;
} }
template<>
const std::complex<float>
NRSMat<std::complex<float> >::dot(const NRVec<std::complex<float> > &rhs) const {
#ifdef DEBUG
if(NN2 != rhs.nn) laerror("incompatible dimensions in std::complex<float> NRSMat<std::complex<float> >::dot(const NRVec<std::complex<float> > &)");
#endif
std::complex<float> dot(0., 0.);
SAME_LOC(*this, rhs);
#ifdef CUDALA
if(location == cpu){
#endif
cblas_cdotc_sub(NN2, v, 1, rhs.v, 1, &dot);
#ifdef CUDALA
}else{
const cuComplex _dot = cublasCdotc(NN2, (cuComplex*)v, 1, (cuComplex*)rhs.v, 1);
TEST_CUBLAS("cublasCdotc");
dot = std::complex<float>(cuCreal(_dot), cuCimag(_dot));
}
#endif
return dot;
}
/***************************************************************************//** /***************************************************************************//**
* compute the Frobenius norm of this real double-precision symmetric matrix * compute the Frobenius norm of this real double-precision symmetric matrix
* @param[in] scalar subtract this scalar value from the diagonal elements before the norm computation * @param[in] scalar subtract this scalar value from the diagonal elements before the norm computation
@@ -645,6 +904,37 @@ const double NRSMat<double>::norm(const double scalar) const {
return std::sqrt(sum); return std::sqrt(sum);
} }
template<>
const float NRSMat<float>::norm(const float scalar) const {
if(!scalar){
float ret(0.);
#ifdef CUDALA
if(location == cpu){
#endif
ret = cblas_snrm2(NN2, v, 1);
#ifdef CUDALA
}else{
ret = cublasSnrm2(NN2, v, 1);
TEST_CUBLAS("cublasSnrm2");
}
#endif
return ret;
}
NOT_GPU(*this);
float sum(0.);
int k(0);
for(register int i=0; i<nn; ++i){
for(register int j=0; j<=i; ++j) {
register float tmp = v[k++];
if(i == j) tmp -= scalar;
sum += tmp*tmp;
}
}
return std::sqrt(sum);
}
/***************************************************************************//** /***************************************************************************//**
* compute the Frobenius norm of this complex double-precision hermitian matrix * compute the Frobenius norm of this complex double-precision hermitian matrix
* @param[in] scalar subtract this scalar value from the diagonal elements before the norm computation * @param[in] scalar subtract this scalar value from the diagonal elements before the norm computation
@@ -680,6 +970,37 @@ const double NRSMat< std::complex<double> >::norm(const std::complex<double> sca
return std::sqrt(sum); return std::sqrt(sum);
} }
template<>
const float NRSMat< std::complex<float> >::norm(const std::complex<float> scalar) const {
if(!(scalar.real()) && !(scalar.imag())){
float ret(0.);
#ifdef CUDALA
if(location == cpu){
#endif
ret = cblas_scnrm2(NN2, v, 1);
#ifdef CUDALA
}else{
ret = cublasScnrm2(NN2, (cuComplex*)v, 1);
TEST_CUBLAS("cublasScnrm2");
}
#endif
return ret;
}
int k(0);
float sum(0.);
std::complex<float> tmp;
for(register int i=0; i<nn; ++i){
for(register int j=0; j<=i; ++j){
tmp = v[k++];
if (i == j) tmp -= scalar;
sum += tmp.real()*tmp.real() + tmp.imag()*tmp.imag();
}
}
return std::sqrt(sum);
}
/***************************************************************************//** /***************************************************************************//**
* symmetric (hermitian) rank-2 update of this matrix stored in packed form * symmetric (hermitian) rank-2 update of this matrix stored in packed form
@@ -709,6 +1030,16 @@ void NRSMat<double>::rank2update(const double alpha, const NRVec<double> &x, con
cblas_dspr2(CblasRowMajor, CblasLower, nn, alpha, x, 1, y, 1, v); cblas_dspr2(CblasRowMajor, CblasLower, nn, alpha, x, 1, y, 1, v);
} }
template<>
void NRSMat<float>::rank2update(const float alpha, const NRVec<float> &x, const NRVec<float> &y) {
#ifdef DEBUG
if(nn != x.size() || nn != y.size()) laerror("incompatible dimensions in NRSMat<float>::rank2update");
#endif
NOT_GPU(*this); NOT_GPU(x); NOT_GPU(y);
copyonwrite();
cblas_sspr2(CblasRowMajor, CblasLower, nn, alpha, x, 1, y, 1, v);
}
template<> template<>
void NRSMat<std::complex<double> >::rank2update(const std::complex<double> alpha, const NRVec<std::complex<double> > &x, const NRVec<std::complex<double> > &y) { void NRSMat<std::complex<double> >::rank2update(const std::complex<double> alpha, const NRVec<std::complex<double> > &x, const NRVec<std::complex<double> > &y) {
#ifdef DEBUG #ifdef DEBUG
@@ -719,6 +1050,16 @@ void NRSMat<std::complex<double> >::rank2update(const std::complex<double> alpha
cblas_zhpr2(CblasRowMajor, CblasLower, nn, &alpha, x, 1, y, 1, v); cblas_zhpr2(CblasRowMajor, CblasLower, nn, &alpha, x, 1, y, 1, v);
} }
template<>
void NRSMat<std::complex<float> >::rank2update(const std::complex<float> alpha, const NRVec<std::complex<float> > &x, const NRVec<std::complex<float> > &y) {
#ifdef DEBUG
if(nn != x.size() || nn != y.size()) laerror("incompatible dimensions in NRSMat<std::complex<float> >::rank2update");
#endif
NOT_GPU(*this); NOT_GPU(x); NOT_GPU(y);
copyonwrite();
cblas_chpr2(CblasRowMajor, CblasLower, nn, &alpha, x, 1, y, 1, v);
}
/***************************************************************************//** /***************************************************************************//**
* for this real double-precision symmetric matrix \f$S\f$ stored in packed form, * for this real double-precision symmetric matrix \f$S\f$ stored in packed form,
@@ -744,6 +1085,25 @@ void NRSMat<double>::axpy(const double alpha, const NRSMat<double> &x) {
#endif #endif
} }
template<>
void NRSMat<float>::axpy(const float alpha, const NRSMat<float> &x) {
#ifdef DEBUG
if(nn != x.nn) laerror("incompatible dimensions in void NRSMat<float>::axpy(const float, const NRSMat<float>&)");
#endif
SAME_LOC(*this, x);
copyonwrite();
#ifdef CUDALA
if(location == cpu){
#endif
cblas_saxpy(NN2, alpha, x.v, 1, v, 1);
#ifdef CUDALA
}else{
cublasSaxpy(NN2, alpha, x.v, 1, v, 1);
TEST_CUBLAS("cublasSaxpy");
}
#endif
}
/***************************************************************************//** /***************************************************************************//**
* for this complex double-precision hermitian matrix \f$H\f$ stored in packed form, * for this complex double-precision hermitian matrix \f$H\f$ stored in packed form,
@@ -771,6 +1131,27 @@ void NRSMat<std::complex<double> >::axpy(const std::complex<double> alpha, const
} }
template<>
void NRSMat<std::complex<float> >::axpy(const std::complex<float> alpha, const NRSMat<std::complex<float> > & x) {
#ifdef DEBUG
if(nn != x.nn) laerror("incompatible dimensions in void NRSMat<std::complex<float> >::axpy(const std::complex<float> , const NRSMat<std::complex<float> >&)");
#endif
SAME_LOC(*this, x);
copyonwrite();
#ifdef CUDALA
if(location == cpu){
#endif
cblas_caxpy(NN2, &alpha, x.v, 1, v, 1);
#ifdef CUDALA
}else{
const cuComplex _alpha = make_cuComplex(alpha.real(), alpha.imag());
cublasCaxpy(NN2, _alpha, (cuComplex*)x.v, 1, (cuComplex*)v, 1);
TEST_CUBLAS("cublasCaxpy");
}
#endif
}
/***************************************************************************//** /***************************************************************************//**
* create hermitian matrix \f$H\f$ from given real double-precision symmetric * create hermitian matrix \f$H\f$ from given real double-precision symmetric
* matrix \f$S\f$ * matrix \f$S\f$
@@ -803,6 +1184,32 @@ NRSMat<std::complex<double> >::NRSMat(const NRSMat<double> &rhs, bool imagpart):
#endif #endif
} }
template<>
NRSMat<std::complex<float> >::NRSMat(const NRSMat<float> &rhs, bool imagpart): nn(rhs.nrows()), count(new int(1)) {
//inconsistent in general case?
const int nnp1 = nn*(nn + 1)/2;
#ifdef CUDALA
location = rhs.getlocation();
if(location == cpu){
#endif
v = new std::complex<float>[nnp1];
memset(v, 0, nnp1*sizeof(std::complex<float>));
cblas_scopy(nnp1, &rhs(0, 0), 1, ((float *)v) + (imagpart?1:0), 2);
#ifdef CUDALA
}else{
v = (std::complex<float>*) gpualloc(nnp1*sizeof(std::complex<float>));
std::complex<float> *_val = gpuputcomplex(CZEROF);
cublasCcopy(nnp1, (cuComplex*)_val, 0, (cuComplex*)v, 1);
TEST_CUBLAS("cublasCcopy");
gpufree(_val);
cublasScopy(nnp1, (float*)(&rhs(0,0)), 1, ((float*)v) + (imagpart?1:0), 2);
TEST_CUBLAS("cublasScopy");
}
#endif
}
/***************************************************************************//** /***************************************************************************//**
@@ -917,9 +1324,15 @@ void NRSMat<T>::storesubmatrix(const NRVec<int> &selection, const NRSMat &rhs)
template<> template<>
NRSMat<double> NRSMat<double>::inverse() {return NRSMat<double>(NRMat<double>(*this).inverse());} NRSMat<double> NRSMat<double>::inverse() {return NRSMat<double>(NRMat<double>(*this).inverse());}
template<>
NRSMat<float> NRSMat<float>::inverse() {return NRSMat<float>(NRMat<float>(*this).inverse());}
template<> template<>
NRSMat<std::complex<double> > NRSMat<std::complex<double> >::inverse() {return NRSMat<std::complex<double> >(NRMat<std::complex<double> >(*this).inverse());} NRSMat<std::complex<double> > NRSMat<std::complex<double> >::inverse() {return NRSMat<std::complex<double> >(NRMat<std::complex<double> >(*this).inverse());}
template<>
NRSMat<std::complex<float> > NRSMat<std::complex<float> >::inverse() {return NRSMat<std::complex<float> >(NRMat<std::complex<float> >(*this).inverse());}
/***************************************************************************//** /***************************************************************************//**
@@ -978,13 +1391,51 @@ NRSMat<std::complex<float> >& NRSMat<std::complex<float> >::conjugateme() {
/***************************************************************************//**
* complexify a given real matrix \f$A\f$ (double and float, by BLAS; the complex default constructor zeroes the imaginary parts)
* @param[in] rhs matrix \f$A\f$ intended for this operation
* @return matrix \f$B\f$ where \f$\Re B=A\f$ and \f$\Im B = 0\f$
******************************************************************************/
template<>
NRSMat<std::complex<double> > complexify<double>(const NRSMat<double> &rhs) {
NRSMat<std::complex<double> > r(rhs.nrows(), rhs.getlocation());
#ifdef CUDALA
if(rhs.getlocation() == cpu){
#endif
cblas_dcopy(rhs.size(), &(rhs[0]), 1, (double*)(&(r[0])), 2);
#ifdef CUDALA
}else{
cublasDcopy(rhs.size(), &(rhs[0]), 1, (double*)(&(r[0])), 2);
TEST_CUBLAS("cublasDcopy");//"NRSMat<std::complex<double> > complexify(const NRSMat<double> &)"
}
#endif
return r;
}
template<>
NRSMat<std::complex<float> > complexify<float>(const NRSMat<float> &rhs) {
NRSMat<std::complex<float> > r(rhs.nrows(), rhs.getlocation());
#ifdef CUDALA
if(rhs.getlocation() == cpu){
#endif
cblas_scopy(rhs.size(), &(rhs[0]), 1, (float*)(&(r[0])), 2);
#ifdef CUDALA
}else{
cublasScopy(rhs.size(), &(rhs[0]), 1, (float*)(&(r[0])), 2);
TEST_CUBLAS("cublasScopy");//"NRSMat<std::complex<float> > complexify(const NRSMat<float> &)"
}
#endif
return r;
}
/***************************************************************************//** /***************************************************************************//**
* forced instantization in the corresponding object file * forced instantization in the corresponding object file
******************************************************************************/ ******************************************************************************/
template class NRSMat<double>; template class NRSMat<double>;
template class NRSMat<std::complex<double> >; template class NRSMat<std::complex<double> >;
//template class NRSMat<float>; template class NRSMat<float>;
//template class NRSMat<std::complex<float> >; template class NRSMat<std::complex<float> >;
template class NRSMat<long long>; template class NRSMat<long long>;
template class NRSMat<long>; template class NRSMat<long>;
template class NRSMat<int>; template class NRSMat<int>;
+210 -22
View File
@@ -235,6 +235,7 @@ public:
//members concerning sparse matrix //members concerning sparse matrix
explicit NRSMat(const SparseMat<T> &rhs); // dense from sparse explicit NRSMat(const SparseMat<T> &rhs); // dense from sparse
explicit NRSMat(const SparseSMat<T> &rhs); // dense from sparse explicit NRSMat(const SparseSMat<T> &rhs); // dense from sparse
explicit NRSMat(const CSRMat<T> &rhs); // dense from sparse (lower triangle)
inline void simplify() {}; //just for compatibility with sparse ones inline void simplify() {}; //just for compatibility with sparse ones
bool issymmetric() const {return 1;} bool issymmetric() const {return 1;}
void swap(NRSMat &rhs) //more efficient swap than via tmp and constructors and operator= void swap(NRSMat &rhs) //more efficient swap than via tmp and constructors and operator=
@@ -387,6 +388,22 @@ inline NRSMat<double> & NRSMat<double>::operator*=(const double &a) {
return *this; return *this;
} }
template<>
inline NRSMat<float> & NRSMat<float>::operator*=(const float &a) {
copyonwrite();
#ifdef CUDALA
if(location == cpu){
#endif
cblas_sscal(NN2, a, v, 1);
#ifdef CUDALA
}else{
cublasSscal(NN2, a, v, 1);
TEST_CUBLAS("cublasSscal");//"NRSMat<float>& NRSMat<float>::operator*=(const float &)"
}
#endif
return *this;
}
/***************************************************************************//** /***************************************************************************//**
* multiply this complex symmetric matrix with complex scalar value * multiply this complex symmetric matrix with complex scalar value
* @param[in] a complex multiplicative factor * @param[in] a complex multiplicative factor
@@ -410,6 +427,24 @@ NRSMat<std::complex<double> >::operator*=(const std::complex<double> &a) {
return *this; return *this;
} }
template<>
inline NRSMat<std::complex<float> > &
NRSMat<std::complex<float> >::operator*=(const std::complex<float> &a) {
copyonwrite();
#ifdef CUDALA
if(location == cpu){
#endif
cblas_cscal(NN2, &a, v, 1);
#ifdef CUDALA
}else{
const cuComplex _a = make_cuComplex(a.real(), a.imag());
cublasCscal(NN2, _a, (cuComplex*)v, 1);
TEST_CUBLAS("cublasCscal");//"NRSMat<std::complex<float> >& NRSMat<std::complex<float> >::operator*=(const std::complex<float> &)"
}
#endif
return *this;
}
/***************************************************************************//** /***************************************************************************//**
* multiply this symmetric matrix of general type <code>T</code> stored in packed form * multiply this symmetric matrix of general type <code>T</code> stored in packed form
@@ -508,6 +543,27 @@ inline NRSMat<double>& NRSMat<double>::operator+=(const NRSMat<double> & rhs) {
return *this; return *this;
} }
template<>
inline NRSMat<float>& NRSMat<float>::operator+=(const NRSMat<float> & rhs) {
#ifdef DEBUG
if(nn != rhs.nn) laerror("incompatible dimensions in NRSMat<float>& NRSMat<float>::operator+=(const NRSMat<float> &)");
#endif
SAME_LOC(*this, rhs);
copyonwrite();
#ifdef CUDALA
if(location == cpu){
#endif
cblas_saxpy(NN2, 1.0, rhs.v, 1, v, 1);
#ifdef CUDALA
}else{
cublasSaxpy(NN2, 1.0, rhs.v, 1, v, 1);
TEST_CUBLAS("cublasSaxpy");//" NRSMat<float>& NRSMat<float>::operator+=(const NRSMat<float> &)"
}
#endif
return *this;
}
/***************************************************************************//** /***************************************************************************//**
* add up this complex symmetric matrix with given symmetric matrix * add up this complex symmetric matrix with given symmetric matrix
* @param[in] rhs complex symmetric matrix to be added * @param[in] rhs complex symmetric matrix to be added
@@ -534,6 +590,27 @@ inline NRSMat<std::complex<double> >& NRSMat<std::complex<double> >::operator+=(
return *this; return *this;
} }
template<>
inline NRSMat<std::complex<float> >& NRSMat<std::complex<float> >::operator+=(const NRSMat<std::complex<float> > & rhs) {
#ifdef DEBUG
if(nn != rhs.nn) laerror("incompatible dimensions in NRSMat<std::complex<float> >& NRSMat<std::complex<float> >::operator+=(const NRSMat<std::complex<float> > &)");
#endif
SAME_LOC(*this, rhs);
copyonwrite();
#ifdef CUDALA
if(location == cpu){
#endif
cblas_caxpy(NN2, &CONEF, rhs.v, 1, v, 1);
#ifdef CUDALA
}else{
cublasCaxpy(NN2, CUONE, (cuComplex*)(rhs.v), 1, (cuComplex*)v, 1);
TEST_CUBLAS("cublasCaxpy");//"NRSMat<std::complex<float> >& NRSMat<std::complex<float> >::operator+=(const NRSMat<std::complex<float> > &)"
}
#endif
return *this;
}
/***************************************************************************//** /***************************************************************************//**
* add up this symmetric matrix of general type <code>T</code> with given symmetric matrix * add up this symmetric matrix of general type <code>T</code> with given symmetric matrix
* @param[in] rhs complex matrix of general type <code>T</code> to be added * @param[in] rhs complex matrix of general type <code>T</code> to be added
@@ -578,6 +655,27 @@ inline NRSMat<double>& NRSMat<double>::operator-=(const NRSMat<double>& rhs) {
return *this; return *this;
} }
template<>
inline NRSMat<float>& NRSMat<float>::operator-=(const NRSMat<float>& rhs) {
#ifdef DEBUG
if(nn != rhs.nn) laerror("incompatible dimensions in NRSMat<float>& NRSMat<float>::operator-=(const NRSMat<float> &)");
#endif
SAME_LOC(*this, rhs);
copyonwrite();
#ifdef CUDALA
if(location == cpu){
#endif
cblas_saxpy(NN2, -1.0, rhs.v, 1, v, 1);
#ifdef CUDALA
}else{
cublasSaxpy(NN2, -1.0, rhs.v, 1, v, 1);
TEST_CUBLAS("cublasSaxpy");//" NRSMat<float>& NRSMat<float>::operator-=(const NRSMat<float> &)"
}
#endif
return *this;
}
/***************************************************************************//** /***************************************************************************//**
* subtracts given complex symmetric matrix from this complex symmetric matrix * subtracts given complex symmetric matrix from this complex symmetric matrix
* @param[in] rhs complex symmetric matrix to be subtracted * @param[in] rhs complex symmetric matrix to be subtracted
@@ -604,6 +702,27 @@ inline NRSMat<std::complex<double> >& NRSMat<std::complex<double> >::operator-=(
return *this; return *this;
} }
template<>
inline NRSMat<std::complex<float> >& NRSMat<std::complex<float> >::operator-=(const NRSMat<std::complex<float> >& rhs) {
#ifdef DEBUG
if(nn != rhs.nn) laerror("incompatible dimensions in NRSMat<std::complex<float> >& NRSMat<std::complex<float> >::operator-=(const NRSMat<std::complex<float> > &)");
#endif
SAME_LOC(*this, rhs);
copyonwrite();
#ifdef CUDALA
if(location == cpu){
#endif
cblas_caxpy(NN2, &CMONEF, rhs.v, 1, v, 1);
#ifdef CUDALA
}else{
cublasCaxpy(NN2, CUMONE, (cuComplex*)(rhs.v), 1, (cuComplex*)v, 1);
TEST_CUBLAS("cublasCaxpy");//"NRSMat<std::complex<float> >& NRSMat<std::complex<float> >::operator-=(const NRSMat<std::complex<float> > &)"
}
#endif
return *this;
}
/***************************************************************************//** /***************************************************************************//**
* subtracts given symmetric matrix of general type <code>T</code> from this symmetric matrix of type <code>T</code> * subtracts given symmetric matrix of general type <code>T</code> from this symmetric matrix of type <code>T</code>
* @param[in] rhs symmetric matrix of general type <code>T</code> to be subtracted * @param[in] rhs symmetric matrix of general type <code>T</code> to be subtracted
@@ -871,6 +990,24 @@ inline const double NRSMat<double>::amax() const {
return ret; return ret;
} }
template<>
inline const float NRSMat<float>::amax() const {
float ret(0.0);
#ifdef CUDALA
if(location == cpu){
#endif
ret = v[cblas_isamax(NN2, v, 1)];
#ifdef CUDALA
}else{
const int pozice = cublasIsamax(NN2, v, 1) ;
TEST_CUBLAS("cublasIsamax");//"float NRSMat<float>::amax()"
gpuget(1, sizeof(float), v + pozice, &ret);
}
#endif
return ret;
}
/***************************************************************************//** /***************************************************************************//**
* for this real symmetric matrix \f$A\f$, determine the * for this real symmetric matrix \f$A\f$, determine the
* first element with smallest absolute value * first element with smallest absolute value
@@ -901,6 +1038,31 @@ inline const double NRSMat<double>::amin() const {
return ret; return ret;
} }
template<>
inline const float NRSMat<float>::amin() const {
float ret(0.0);
#ifdef CUDALA
if(location == cpu){
#endif
// idamin seems not to be supported
float val(0.0);
int index(-1);
ret = std::numeric_limits<float>::max();
for(register size_t i = 0; i < NN2; i++){
val = std::abs(v[i]);
if(val < ret){ index = i; ret = val; }
}
ret = v[index];
#ifdef CUDALA
}else{
const int pozice = cublasIsamin(nn, v, 1);
TEST_CUBLAS("cublasIsamin");//"float NRSMat<float>::amin()"
gpuget(1, sizeof(float), v + pozice, &ret);
}
#endif
return ret;
}
/***************************************************************************//** /***************************************************************************//**
* for this complex symmetric matrix \f$A\f$, determine the * for this complex symmetric matrix \f$A\f$, determine the
* first element with largest "absolute value" * first element with largest "absolute value"
@@ -923,6 +1085,23 @@ inline const std::complex<double> NRSMat< std::complex<double> >::amax() const{
return ret; return ret;
} }
template<>
inline const std::complex<float> NRSMat< std::complex<float> >::amax() const{
std::complex<float> ret(0., 0.);
#ifdef CUDALA
if(location == cpu){
#endif
ret = v[cblas_icamax(NN2, v, 1)];
#ifdef CUDALA
}else{
const int pozice = cublasIcamax(NN2, (cuComplex*)v, 1);
TEST_CUBLAS("cublasIcamax");//"std::complex<float> NRSMat<std::complex<float> >::amax()"
gpuget(1, sizeof(std::complex<float>), v + pozice, &ret);
}
#endif
return ret;
}
/***************************************************************************//** /***************************************************************************//**
* for this complex symmetric matrix \f$A\f$, determine the * for this complex symmetric matrix \f$A\f$, determine the
* first element with smallest "absolute value" * first element with smallest "absolute value"
@@ -956,6 +1135,34 @@ inline const std::complex<double> NRSMat<std::complex<double> >::amin() const{
return ret; return ret;
} }
template<>
inline const std::complex<float> NRSMat<std::complex<float> >::amin() const{
std::complex<float> ret(0., 0.);
#ifdef CUDALA
if(location == cpu){
#endif
// izamin seems not to be supported
int index(0);
float val(0.0), min_val(0.0);
std::complex<float> z_val(0.0, 0.0);
min_val = std::numeric_limits<float>::max();
for(register size_t i = 0; i < NN2; i++){
z_val = v[i];
val = std::abs(z_val.real()) + std::abs(z_val.imag());
if(val < min_val){ index = i; min_val = val; }
}
ret = v[index];
#ifdef CUDALA
}else{
const int pozice = cublasIcamin(nn, (cuComplex*)v, 1);
TEST_CUBLAS("cublasIcamin");//"std::complex<float> NRSMat<std::complex<float> >::amin()"
gpuget(1, sizeof(std::complex<float>), v + pozice, &ret);
}
#endif
return ret;
}
/***************************************************************************//** /***************************************************************************//**
* @return pointer of general type T to the underlying data structure * @return pointer of general type T to the underlying data structure
******************************************************************************/ ******************************************************************************/
@@ -1215,28 +1422,9 @@ NRSMat<std::complex<T> > complexify(const NRSMat<T> &rhs) {
} }
/***************************************************************************//** //BLAS versions for double and float are in smat.cc (a full specialization defined in the header would be multiply defined)
* complexify a given double-precision real matrix \f$A\f$ template<> NRSMat<std::complex<double> > complexify<double>(const NRSMat<double> &rhs);
* @param[in] rhs matrix \f$A\f$ intended for this operation template<> NRSMat<std::complex<float> > complexify<float>(const NRSMat<float> &rhs);
* @return matrix \f$B\f$ where \f$\Re B=A\f$ and \f$\Im B = 0\f$
******************************************************************************/
/*
template<>
NRSMat<std::complex<double> > complexify(const NRSMat<double> &rhs) {
NRSMat<std::complex<double> > r(rhs.nrows(), rhs.getlocation());
#ifdef CUDALA
if(rhs.getlocation() == cpu){
#endif
cblas_dcopy(rhs.size(), &(rhs[0]), 1, (double*)(&(r[0])), 2);
#ifdef CUDALA
}else{
cublasDcopy(rhs.size(), &(rhs[0]), 1, (double*)(&(r[0])), 2);
TEST_CUBLAS("cublasDcopy");//"NRSMat<std::complex<double> > complexify(const NRSMat<double> &)"
}
#endif
return r;
}
*/
/***************************************************************************//** /***************************************************************************//**
* output operator * output operator
* @param[in,out] s output stream * @param[in,out] s output stream
+92 -18
View File
@@ -334,7 +334,7 @@ return nonzero; //number of (in principle) nonzero elements
template <class T> template <class T>
void SparseMat<T>::simplify(const double sparseepsilon) void SparseMat<T>::simplify(const typename LA_traits<T>::normtype sparseepsilon)
{ {
unsigned int n; unsigned int n;
if(!list) return; if(!list) return;
@@ -431,7 +431,7 @@ void SparseMat<T>::addsafe(const SPMatindex n, const SPMatindex m, const T elem)
#ifdef debug #ifdef debug
if(n<0||n>=nn||m<0||m>=mm) laerror("SparseMat out of range"); if(n<0||n>=nn||m<0||m>=mm) laerror("SparseMat out of range");
#endif #endif
if(std::abs(elem)<=SPARSEEPSILON) return; if(std::abs(elem)<=LA_precision<typename LA_traits<T>::normtype>::sparse_epsilon()) return;
if(!count) {count=new int; *count=1; list=NULL;} //blank new matrix if(!count) {count=new int; *count=1; list=NULL;} //blank new matrix
else copyonwrite(); //makes also unsort else copyonwrite(); //makes also unsort
add(n,m,elem); add(n,m,elem);
@@ -482,7 +482,7 @@ register matel<T> *l=rhs.list;
while(l) while(l)
{ {
if(rhs.symmetric || lower && l->row <=l->col || !lower && l->row >=l->col) if(rhs.symmetric || lower && l->row <=l->col || !lower && l->row >=l->col)
if(std::abs(l->elem)>SPARSEEPSILON) if(std::abs(l->elem)>LA_precision<typename LA_traits<T>::normtype>::sparse_epsilon())
add( l->row,l->col,sign=='+'?l->elem:- l->elem); add( l->row,l->col,sign=='+'?l->elem:- l->elem);
l=l->next; l=l->next;
} }
@@ -501,14 +501,14 @@ register matel<T> *l=rhs.list;
if(symmetrize) if(symmetrize)
while(l) while(l)
{ {
if(std::abs(l->elem)>SPARSEEPSILON) if(std::abs(l->elem)>LA_precision<typename LA_traits<T>::normtype>::sparse_epsilon())
{add( l->row,l->col,l->elem); if( l->row!=l->col) add( l->col,l->row,l->elem);} {add( l->row,l->col,l->elem); if( l->row!=l->col) add( l->col,l->row,l->elem);}
l=l->next; l=l->next;
} }
else else
while(l) while(l)
{ {
if(std::abs(l->elem)>SPARSEEPSILON) if(std::abs(l->elem)>LA_precision<typename LA_traits<T>::normtype>::sparse_epsilon())
add( l->row,l->col,l->elem); add( l->row,l->col,l->elem);
l=l->next; l=l->next;
} }
@@ -528,14 +528,14 @@ register matel<T> *l=rhs.list;
if(symmetrize) if(symmetrize)
while(l) while(l)
{ {
if(std::abs(l->elem)>SPARSEEPSILON) if(std::abs(l->elem)>LA_precision<typename LA_traits<T>::normtype>::sparse_epsilon())
{add( l->row,l->col,- l->elem); if( l->row!=l->col) add( l->col,l->row,- l->elem);} {add( l->row,l->col,- l->elem); if( l->row!=l->col) add( l->col,l->row,- l->elem);}
l=l->next; l=l->next;
} }
else else
while(l) while(l)
{ {
if(std::abs(l->elem)>SPARSEEPSILON) if(std::abs(l->elem)>LA_precision<typename LA_traits<T>::normtype>::sparse_epsilon())
add( l->row,l->col,- l->elem); add( l->row,l->col,- l->elem);
l=l->next; l=l->next;
} }
@@ -559,7 +559,7 @@ SPMatindex i,j;
for(i=0;i<nn;++i) for(i=0;i<nn;++i)
for(j=0; j<mm;++j) for(j=0; j<mm;++j)
{register T t(rhs(i,j)); {register T t(rhs(i,j));
if( std::abs(t)>SPARSEEPSILON) if( std::abs(t)>LA_precision<typename LA_traits<T>::normtype>::sparse_epsilon())
add(i,j,t); add(i,j,t);
} }
} }
@@ -598,12 +598,12 @@ if(nn==mm) //square
else //diagonal of A^TA, assuming it has been simplified (only one entry per position), will be used as preconditioner only anyway else //diagonal of A^TA, assuming it has been simplified (only one entry per position), will be used as preconditioner only anyway
while(l) while(l)
{ {
(*rr)[l->col] += l->elem*l->elem *(l->col!=l->row && symmetric?2.:1.); (*rr)[l->col] += l->elem*l->elem *(T)(l->col!=l->row && symmetric?2:1);
l= l->next; l= l->next;
} }
if(divide) if(divide)
{ {
for(unsigned int i=0; i<mm; ++i) if((*rr)[i]!=0.) r[i]/=(*rr)[i]; for(unsigned int i=0; i<mm; ++i) if((*rr)[i]!=(T)0) r[i]/=(*rr)[i];
delete rr; delete rr;
} }
return divide?NULL:&r[0]; return divide?NULL:&r[0];
@@ -693,7 +693,7 @@ SparseMat<T> & SparseMat<T>::operator=(const T &a)
if(!count ||nn<=0||mm<=0) laerror("assignment of scalar to undefined sparse matrix"); if(!count ||nn<=0||mm<=0) laerror("assignment of scalar to undefined sparse matrix");
if(nn!=mm) laerror("assignment of scalar to non-square sparse matrix"); if(nn!=mm) laerror("assignment of scalar to non-square sparse matrix");
resize(nn,mm);//clear resize(nn,mm);//clear
if(std::abs(a)<=SPARSEEPSILON) return *this; if(std::abs(a)<=LA_precision<typename LA_traits<T>::normtype>::sparse_epsilon()) return *this;
SPMatindex i; SPMatindex i;
for(i=0;i<nn;++i) add(i,i,a); for(i=0;i<nn;++i) add(i,i,a);
return *this; return *this;
@@ -741,7 +741,7 @@ for(i=0;i<nn;++i)
for(j=0; j<=i;++j) for(j=0; j<=i;++j)
{register T t; {register T t;
if( if(
std::abs(t=rhs(i,j))>SPARSEEPSILON std::abs(t=rhs(i,j))>LA_precision<typename LA_traits<T>::normtype>::sparse_epsilon()
) add(i,j,t); ) add(i,j,t);
} }
} }
@@ -774,7 +774,7 @@ matel<T> *l=list;
while(l) //include the mirror picture of elements into the list while(l) //include the mirror picture of elements into the list
{ {
if( if(
std::abs(l->elem)>SPARSEEPSILON && std::abs(l->elem)>LA_precision<typename LA_traits<T>::normtype>::sparse_epsilon() &&
l->row!=l->col) add(l->col,l->row,l->elem); //not OK for complex-hermitean l->row!=l->col) add(l->col,l->row,l->elem); //not OK for complex-hermitean
l=l->next; l=l->next;
} }
@@ -812,12 +812,32 @@ while(l)
return r; return r;
} }
template<>
const float SparseMat<float>::dot(const NRMat<float> &rhs) const
{
float r=0;
matel<float> *l=list;
while(l)
{
r+= l->elem*rhs[l->row][l->col];
if(symmetric&&l->row!=l->col) r+=l->elem*rhs[l->col][l->row];
l=l->next;
}
return r;
}
template<> template<>
void NRMat<std::complex<double> >::gemm(const std::complex<double> &beta, const SparseMat<std::complex<double> > &a, const char transa, const NRMat<std::complex<double> > &b, const char transb, const std::complex<double> &alpha) void NRMat<std::complex<double> >::gemm(const std::complex<double> &beta, const SparseMat<std::complex<double> > &a, const char transa, const NRMat<std::complex<double> > &b, const char transb, const std::complex<double> &alpha)
{ {
laerror("not implemented yet"); laerror("not implemented yet");
} }
template<>
void NRMat<std::complex<float> >::gemm(const std::complex<float> &beta, const SparseMat<std::complex<float> > &a, const char transa, const NRMat<std::complex<float> > &b, const char transb, const std::complex<float> &alpha)
{
laerror("not implemented yet");
}
template<> template<>
void NRMat<double>::gemm(const double &beta, const SparseMat<double> &a, const char transa, const NRMat<double> &b, const char transb, const double &alpha) void NRMat<double>::gemm(const double &beta, const SparseMat<double> &a, const char transa, const NRMat<double> &b, const char transb, const double &alpha)
@@ -867,6 +887,54 @@ else
} }
template<>
void NRMat<float>::gemm(const float &beta, const SparseMat<float> &a, const char transa, const NRMat<float> &b, const char transb, const float &alpha)
{
bool transpa = tolower(transa)!='n'; //not OK for complex
bool transpb = tolower(transb)!='n'; //not OK for complex
if(nn!=(int)(transpa?a.ncols():a.nrows()) | mm!= (transpb?b.nrows():b.ncols()) || (int)(transpa?a.nrows():a.ncols()) != (transpb?b.ncols():b.nrows())) laerror("incompatible sizes in gemm");
copyonwrite();
if(beta!=(float)0) (*this) *= beta;
else memset(v,0,nn*mm*sizeof(float));
matel<float> *l=a.getlist();
if(alpha==(float)0 || !l) return;
//raw pointers to the full matrices
const float *bp= b;
float *p= *this;
int ldb=(transpb?b.ncols():1);
int bstep=(transpb?1:b.ncols());
int len=(transpb?b.nrows():b.ncols());
if(a.issymmetric())
{
while(l)
{
cblas_saxpy(len, alpha*l->elem , bp+l->row*bstep, ldb, p+l->col*mm, 1);
if(l->row!=l->col) cblas_saxpy(len, alpha*l->elem , bp+l->col*bstep, ldb, p+l->row*mm, 1);
l=l->next;
}
}
else
{
if(transpa)
while(l)
{
cblas_saxpy(len, alpha*l->elem , bp+l->row*bstep, ldb, p+l->col*mm, 1);
l=l->next;
}
else
while(l)
{
cblas_saxpy(len, alpha*l->elem , bp+l->col*bstep, ldb, p+l->row*mm, 1);
l=l->next;
}
}
}
template<class T> template<class T>
@@ -1036,7 +1104,7 @@ if(transp)
while(l) while(l)
{ {
register T t=alpha*l->elem; register T t=alpha*l->elem;
if(std::abs(t)>SPARSEEPSILON) if(std::abs(t)>LA_precision<typename LA_traits<T>::normtype>::sparse_epsilon())
add( l->col,l->row,t); add( l->col,l->row,t);
l=l->next; l=l->next;
} }
@@ -1044,7 +1112,7 @@ else
while(l) while(l)
{ {
register T t=alpha*l->elem; register T t=alpha*l->elem;
if(std::abs(t)>SPARSEEPSILON) if(std::abs(t)>LA_precision<typename LA_traits<T>::normtype>::sparse_epsilon())
add( l->row,l->col,t); add( l->row,l->col,t);
l=l->next; l=l->next;
} }
@@ -1329,7 +1397,7 @@ incsize(rhs.nrows(), rhs.ncols());
T tmp; T tmp;
for(SPMatindex i=0; i<(SPMatindex)rhs.nrows(); ++i) for(SPMatindex i=0; i<(SPMatindex)rhs.nrows(); ++i)
for(SPMatindex j=0; j<(SPMatindex)rhs.ncols(); ++j) for(SPMatindex j=0; j<(SPMatindex)rhs.ncols(); ++j)
if(std::abs(tmp=rhs(i,j))>SPARSEEPSILON) add(n0+i,m0+j,tmp); if(std::abs(tmp=rhs(i,j))>LA_precision<typename LA_traits<T>::normtype>::sparse_epsilon()) add(n0+i,m0+j,tmp);
return *this; return *this;
} }
@@ -1345,7 +1413,7 @@ T tmp;
incsize(rhs.nrows(), rhs.ncols()); incsize(rhs.nrows(), rhs.ncols());
for(SPMatindex i=0; i<(SPMatindex)rhs.nrows(); ++i) for(SPMatindex i=0; i<(SPMatindex)rhs.nrows(); ++i)
for(SPMatindex j=0; j<=i; ++j) //symmetric storage: one triangle only for(SPMatindex j=0; j<=i; ++j) //symmetric storage: one triangle only
if(std::abs(tmp=rhs(i,j))>SPARSEEPSILON) add(n0+i,m0+j,tmp); if(std::abs(tmp=rhs(i,j))>LA_precision<typename LA_traits<T>::normtype>::sparse_epsilon()) add(n0+i,m0+j,tmp);
return *this; return *this;
} }
@@ -1364,7 +1432,7 @@ incsize(rhs.nrows(), rhs.ncols());
register matel<T> *l=rhs.list; register matel<T> *l=rhs.list;
while(l) while(l)
{ {
if(std::abs(l->elem)>SPARSEEPSILON) if(std::abs(l->elem)>LA_precision<typename LA_traits<T>::normtype>::sparse_epsilon())
add(n0+l->row,m0+l->col,l->elem); add(n0+l->row,m0+l->col,l->elem);
l=l->next; l=l->next;
} }
@@ -1429,8 +1497,12 @@ INSTANTIZE(std::complex<double>) //some functions are not OK for hermitean matri
//// forced instantization in the corresponding object file //// forced instantization in the corresponding object file
template class SparseMat<double>; template class SparseMat<double>;
template class SparseMat<std::complex<double> >; template class SparseMat<std::complex<double> >;
template class SparseMat<float>;
template class SparseMat<std::complex<float> >;
template class SemiSparseMat<double>; template class SemiSparseMat<double>;
template class SemiSparseMat<std::complex<double> >; template class SemiSparseMat<std::complex<double> >;
template class SemiSparseMat<float>;
template class SemiSparseMat<std::complex<float> >;
#define INSTANTIZE(T) \ #define INSTANTIZE(T) \
@@ -1443,6 +1515,8 @@ template void NRVec<T>::gemv(const T beta, const SparseMat<T> &a, const char tra
INSTANTIZE(double) INSTANTIZE(double)
INSTANTIZE(std::complex<double>) INSTANTIZE(std::complex<double>)
INSTANTIZE(float)
INSTANTIZE(std::complex<float>)
}//namespace }//namespace
+2 -1
View File
@@ -80,6 +80,7 @@ public:
inline int getcount() const {return count?*count:0;} inline int getcount() const {return count?*count:0;}
explicit SparseMat(const NRMat<T> &rhs); //construct from a dense one explicit SparseMat(const NRMat<T> &rhs); //construct from a dense one
explicit SparseMat(const NRSMat<T> &rhs); //construct from a dense symmetric one explicit SparseMat(const NRSMat<T> &rhs); //construct from a dense symmetric one
explicit SparseMat(const CSRMat<T> &rhs); //full storage, not symmetric
SparseMat & operator=(const SparseMat &rhs); SparseMat & operator=(const SparseMat &rhs);
SparseMat & operator=(const T &a); //assign a to diagonal SparseMat & operator=(const T &a); //assign a to diagonal
void identity() {*this = (T)1;}; void identity() {*this = (T)1;};
@@ -141,7 +142,7 @@ public:
unsigned int length() const; unsigned int length() const;
void copyonwrite(bool detachonly=false, bool deep=true); void copyonwrite(bool detachonly=false, bool deep=true);
void clear() {bool sym=symmetric; resize(nn,mm); symmetric=sym;} //zero matrix of the same dimensions and symmetry void clear() {bool sym=symmetric; resize(nn,mm); symmetric=sym;} //zero matrix of the same dimensions and symmetry
void simplify(const double sparseepsilon=SPARSEEPSILON); void simplify(const typename LA_traits<T>::normtype sparseepsilon=LA_precision<typename LA_traits<T>::normtype>::sparse_epsilon());
const T trace() const; const T trace() const;
const typename LA_traits<T>::normtype norm(const T scalar=(T)0) const; //is const only mathematically, not in internal implementation - we have to simplify first const typename LA_traits<T>::normtype norm(const T scalar=(T)0) const; //is const only mathematically, not in internal implementation - we have to simplify first
unsigned int sort(int type) const; unsigned int sort(int type) const;
+7 -1
View File
@@ -239,7 +239,7 @@ for(SPMatindex i=0; i<nn; ++i)
} }
if(divide) if(divide)
{ {
for(unsigned int i=0; i<nn; ++i) if((*rr)[i]!=0.) r[i]/=(*rr)[i]; for(unsigned int i=0; i<nn; ++i) if((*rr)[i]!=(T)0) r[i]/=(*rr)[i];
delete rr; delete rr;
} }
return divide?NULL:&r[0]; return divide?NULL:&r[0];
@@ -352,12 +352,18 @@ INSTANTIZE(complex<double>)
//// forced instantization of functions in the header in the corresponding object file //// forced instantization of functions in the header in the corresponding object file
template class SparseSMat<double>; template class SparseSMat<double>;
template class SparseSMat<std::complex<double> >; template class SparseSMat<std::complex<double> >;
template class SparseSMat<float>;
template class SparseSMat<std::complex<float> >;
template SparseSMat<double> NRMat<double>::operator*(const SparseSMat<double> &rhs) const; template SparseSMat<double> NRMat<double>::operator*(const SparseSMat<double> &rhs) const;
template SparseSMat<float> NRMat<float>::operator*(const SparseSMat<float> &rhs) const;
template SparseSMat<std::complex<double> > NRMat<std::complex<double> >::operator*(const SparseSMat<std::complex<double> > &rhs) const; template SparseSMat<std::complex<double> > NRMat<std::complex<double> >::operator*(const SparseSMat<std::complex<double> > &rhs) const;
template SparseSMat<std::complex<float> > NRMat<std::complex<float> >::operator*(const SparseSMat<std::complex<float> > &rhs) const;
/*activate this if needed /*activate this if needed
template void SparseSMat<NRMat<double> >::put(int fd, bool dimen, bool transp, bool orcaformat) const; template void SparseSMat<NRMat<double> >::put(int fd, bool dimen, bool transp, bool orcaformat) const;
template void SparseSMat<NRMat<float> >::put(int fd, bool dimen, bool transp, bool orcaformat) const;
template void SparseSMat<NRMat<double> >::get(int fd, bool dimen, bool transp, bool orcaformat); template void SparseSMat<NRMat<double> >::get(int fd, bool dimen, bool transp, bool orcaformat);
template void SparseSMat<NRMat<float> >::get(int fd, bool dimen, bool transp, bool orcaformat);
*/ */
+9 -9
View File
@@ -200,7 +200,7 @@ count(new int(1))
v= new std::map<SPMatindex,T> * [nn]; v= new std::map<SPMatindex,T> * [nn];
memset(v,0,nn*sizeof(std::map<SPMatindex,T> *)); memset(v,0,nn*sizeof(std::map<SPMatindex,T> *));
int i,j; int i,j;
for(i=0; i<nn; ++i) for(j=0; j<=i; ++j) if(std::abs(rhs(i,j))>SPARSEEPSILON) (*this).add(i,j,rhs(i,j),true); for(i=0; i<nn; ++i) for(j=0; j<=i; ++j) if(std::abs(rhs(i,j))>LA_precision<typename LA_traits<T>::normtype>::sparse_epsilon()) (*this).add(i,j,rhs(i,j),true);
} }
//from SparseMat: duplicates summed; a symmetric SparseMat (one triangle stored, complex symmetric) is unfolded without conjugation //from SparseMat: duplicates summed; a symmetric SparseMat (one triangle stored, complex symmetric) is unfolded without conjugation
@@ -227,7 +227,7 @@ count(new int(1))
v= new std::map<SPMatindex,T> * [nn]; v= new std::map<SPMatindex,T> * [nn];
memset(v,0,nn*sizeof(std::map<SPMatindex,T> *)); memset(v,0,nn*sizeof(std::map<SPMatindex,T> *));
int i,j; int i,j;
for(i=0; i<nn; ++i) for(j=0; j<mm; ++j) if(std::abs(rhs(i,j))>SPARSEEPSILON) (*this).add(i,j,rhs(i,j),false); for(i=0; i<nn; ++i) for(j=0; j<mm; ++j) if(std::abs(rhs(i,j))>LA_precision<typename LA_traits<T>::normtype>::sparse_epsilon()) (*this).add(i,j,rhs(i,j),false);
} }
@@ -437,7 +437,7 @@ for(SPMatindex i=0; i<nn; ++i) if(v[i])
std::list<SPMatindex> l; std::list<SPMatindex> l;
typename std::map<SPMatindex,T>::iterator p; typename std::map<SPMatindex,T>::iterator p;
for(p=v[i]->begin(); p!=v[i]->end(); ++p) for(p=v[i]->begin(); p!=v[i]->end(); ++p)
if(std::abs(p->second) < SPARSEEPSILON) l.push_front(p->first); else ++count; if(std::abs(p->second) < LA_precision<typename LA_traits<T>::normtype>::sparse_epsilon()) l.push_front(p->first); else ++count;
typename std::list<SPMatindex>::iterator q; typename std::list<SPMatindex>::iterator q;
for(q=l.begin(); q!=l.end(); ++q) v[i]->erase(*q); for(q=l.begin(); q!=l.end(); ++q) v[i]->erase(*q);
if(v[i]->size() == 0) {delete v[i]; v[i]=NULL;} if(v[i]->size() == 0) {delete v[i]; v[i]=NULL;}
@@ -548,7 +548,7 @@ for(SPMatindex i=0; i<nn; ++i)
//std::cout <<"Permuted upper triangle matrix\n"<<r; //std::cout <<"Permuted upper triangle matrix\n"<<r;
//SparseSMat<T> r0(r);r.copyonwrite(); //SparseSMat<T> r0(r);r.copyonwrite();
//perform complex, positive semidefinite Cholesky with thresholding by SPARSEEPSILON //perform complex, positive semidefinite Cholesky with thresholding by LA_precision<>::sparse_epsilon()
SPMatindex i,j,k; SPMatindex i,j,k;
int rank=0; int rank=0;
for(k=0; k<nn; ++k) for(k=0; k<nn; ++k)
@@ -557,7 +557,7 @@ for(k=0; k<nn; ++k)
typename std::map<SPMatindex,T>::iterator p; typename std::map<SPMatindex,T>::iterator p;
p= r.v[k]->find(k); p= r.v[k]->find(k);
if(p==r.v[k]->end()) continue; //must not break due to the a priori pivoting if(p==r.v[k]->end()) continue; //must not break due to the a priori pivoting
if(LA_traits<T>::realpart(p->second) < CHOLESKYEPSILON) continue; //must not break due to the a priori pivoting if(LA_traits<T>::realpart(p->second) < LA_precision<typename LA_traits<T>::normtype>::cholesky_epsilon()) continue; //must not break due to the a priori pivoting
++rank; ++rank;
typename LA_traits<T>::normtype tmp = std::sqrt(LA_traits<T>::realpart(p->second)); typename LA_traits<T>::normtype tmp = std::sqrt(LA_traits<T>::realpart(p->second));
p->second = tmp; p->second = tmp;
@@ -572,7 +572,7 @@ for(k=0; k<nn; ++k)
{ {
T akj = LA_traits<T>::conjugate(linek[j]); T akj = LA_traits<T>::conjugate(linek[j]);
NRVec<int> linek_used(0,nn); NRVec<int> linek_used(0,nn);
if(std::abs(akj)>SPARSEEPSILON) if(std::abs(akj)>LA_precision<typename LA_traits<T>::normtype>::sparse_epsilon())
{ {
for(p=r.v[j]->begin(); p!=r.v[j]->end(); ++p) for(p=r.v[j]->begin(); p!=r.v[j]->end(); ++p)
{ {
@@ -581,7 +581,7 @@ for(k=0; k<nn; ++k)
} }
//subtract also elements nonzero in line k but non-existent in line j //subtract also elements nonzero in line k but non-existent in line j
for(i=j; i<nn; ++i) for(i=j; i<nn; ++i)
if(!linek_used[i] && std::abs(linek[i]) > SPARSEEPSILON) if(!linek_used[i] && std::abs(linek[i]) > LA_precision<typename LA_traits<T>::normtype>::sparse_epsilon())
{ {
(*r.v[j])[i] = -akj * linek[i]; (*r.v[j])[i] = -akj * linek[i];
} }
@@ -611,7 +611,7 @@ for(k=0; k<nn; ++k)
typename std::map<SPMatindex,T> *vnew = new typename std::map<SPMatindex,T>; typename std::map<SPMatindex,T> *vnew = new typename std::map<SPMatindex,T>;
for(p=r.v[k]->begin(); p!=r.v[k]->end(); ++p) for(p=r.v[k]->begin(); p!=r.v[k]->end(); ++p)
{ {
if(std::abs(p->second) > SPARSEEPSILON) (*vnew)[pivot[p->first]] = p->second; if(std::abs(p->second) > LA_precision<typename LA_traits<T>::normtype>::sparse_epsilon()) (*vnew)[pivot[p->first]] = p->second;
} }
delete r.v[k]; delete r.v[k];
r.v[k]=vnew; r.v[k]=vnew;
@@ -636,7 +636,7 @@ for(SPMatindex i=0; i<lhs.size(); ++i)
{ {
T x=lhs[i]*(conjugate?LA_traits<T>::conjugate(rhs[j]):rhs[j])*scale; T x=lhs[i]*(conjugate?LA_traits<T>::conjugate(rhs[j]):rhs[j])*scale;
if(i==j) x+=LA_traits<T>::conjugate(x); //diagonal of M + M^\dagger, add() mirrors only off-diagonal elements if(i==j) x+=LA_traits<T>::conjugate(x); //diagonal of M + M^\dagger, add() mirrors only off-diagonal elements
if(std::abs(x)>SPARSEEPSILON) r.add(i,j,x); if(std::abs(x)>LA_precision<typename LA_traits<T>::normtype>::sparse_epsilon()) r.add(i,j,x);
} }
} }
return r; return r;
+150 -23
View File
@@ -19,32 +19,159 @@
#include "mat.h" #include "mat.h"
#ifndef NO_STRASSEN
namespace LA { namespace LA {
/*Strassen algorithm*/ /*Strassen algorithm in the Winograd variant (7 products, 15 additions), recursion on strided quadrants of the original matrices,
// called routine is fortran-compatible leaf products by BLAS gemm
extern "C" void fmm(const char c_transa,const char c_transb,const int m,const int n,const int k,const double alpha, V. Strassen, Gaussian elimination is not optimal, Numer. Math. 13 (1969) 354-356
const double *a,const int lda,const double *b,const int ldb,const double beta,double *c,const int ldc, S. Winograd, On multiplication of 2x2 matrices, Linear Algebra Appl. 4 (1971) 381-388
double *d_aux,int i_naux); schedule with 2 temporaries per level: B. Boyer, J.-G. Dumas, C. Pernet, W. Zhou, Memory efficient scheduling of
extern "C" void strassen_cutoff(int c, int c1, int c2, int c3); Strassen-Winograd's matrix multiplication algorithm, Proc. ISSAC 2009, 55-62
odd dimensions by dynamic peeling: S. Huss-Lederman, E. M. Jacobson, J. R. Johnson, A. Tsao, T. Turnbull,
Implementation of Strassen's algorithm for matrix multiplication, Proc. Supercomputing '96
*/
template<> //device-dependent primitives: leaf gemm, quadrant addition, workspace (GPU version: cublas?gemm, cublas?geam, gpualloc/gpufree)
void NRMat<double>::s_cutoff(const int c, const int c1, const int c2, const int c3) const static inline CBLAS_TRANSPOSE s_trans(const char t) {return t=='n'?CblasNoTrans:(t=='c'?CblasConjTrans:CblasTrans);}
{ strassen_cutoff(c,c1,c2,c3);}
template<> static inline void s_gemm(const char ta, const char tb, const int m, const int n, const int k, const double alpha, const double *a, const int lda, const double *b, const int ldb, const double beta, double *c, const int ldc)
void NRMat<double>::strassen(const double beta, const NRMat<double> &a, const char transa, const NRMat<double> &b, const char transb, const double alpha) {cblas_dgemm(CblasRowMajor,s_trans(ta),s_trans(tb),m,n,k,alpha,a,lda,b,ldb,beta,c,ldc);}
static inline void s_gemm(const char ta, const char tb, const int m, const int n, const int k, const float alpha, const float *a, const int lda, const float *b, const int ldb, const float beta, float *c, const int ldc)
{cblas_sgemm(CblasRowMajor,s_trans(ta),s_trans(tb),m,n,k,alpha,a,lda,b,ldb,beta,c,ldc);}
static inline void s_gemm(const char ta, const char tb, const int m, const int n, const int k, const std::complex<double> alpha, const std::complex<double> *a, const int lda, const std::complex<double> *b, const int ldb, const std::complex<double> beta, std::complex<double> *c, const int ldc)
{cblas_zgemm(CblasRowMajor,s_trans(ta),s_trans(tb),m,n,k,&alpha,a,lda,b,ldb,&beta,c,ldc);}
static inline void s_gemm(const char ta, const char tb, const int m, const int n, const int k, const std::complex<float> alpha, const std::complex<float> *a, const int lda, const std::complex<float> *b, const int ldb, const std::complex<float> beta, std::complex<float> *c, const int ldc)
{cblas_cgemm(CblasRowMajor,s_trans(ta),s_trans(tb),m,n,k,&alpha,a,lda,b,ldb,&beta,c,ldc);}
//z = x+y or x-y, r x c blocks with leading dimensions; z may coincide with x or y (one pass, memory bound)
template<typename T>
static void s_add(const int r, const int c, T *z, const int ldz, const T *x, const int ldx, const T *y, const int ldy, const bool minus)
{ {
int l(transa=='n'?a.nn:a.mm); for(int i=0; i<r; ++i)
int k(transa=='n'?a.mm:a.nn); {
int kk(transb=='n'?b.nn:b.mm); T *zz=z+(size_t)i*ldz; const T *xx=x+(size_t)i*ldx; const T *yy=y+(size_t)i*ldy;
int ll(transb=='n'?b.mm:b.nn); if(minus) for(int j=0; j<c; ++j) zz[j]=xx[j]-yy[j];
else for(int j=0; j<c; ++j) zz[j]=xx[j]+yy[j];
if(l!=nn|| ll!=mm||k!=kk) laerror("incompatible (or undefined size) matrices in strassen"); }
copyonwrite();
//swap transpositions and order of matrices
fmm(transb,transa,mm,nn,k,alpha,b,b.mm, a, a.mm, beta,*this, mm,NULL,0);
} }
}//namespace
template<typename T> static T *s_alloc(const size_t n) {return n? new T[n] : NULL;}
template<typename T> static void s_free(T *p) {if(p) delete[] p;}
//operand op(M) of the product, op = identity ('n'), transposition ('t') or conjugate transposition ('c'),
//M stored row-major at p with leading dimension ld; the recursion is formulated in op() space and never transposes data:
//- at(i,j) is the address of element (i,j) of op(M), i.e. M(i,j) for 'n' and M(j,i) for 't','c';
// a block of op(M) starting at (i,j) is then the block of M starting at at(i,j) with the same ld and flag
// (quadrant (op M)_{IJ} = op(M_{JI}), the stored quadrant being swapped for 't','c' and conjugated by gemm for 'c')
//- sums of quadrants are formed elementwise in the stored layout (S stored = M_{JI} + M_{LK} for 't','c'), which is correct
// because op(X+Y)=op(X)+op(Y), so the temporaries keep the flag of their operand and gemm applies op() at the leaves only
template<typename T>
struct s_op
{
const T *p; int ld; char t;
const T *at(const int i, const int j) const {return t=='n' ? p+(size_t)i*ld+j : p+(size_t)j*ld+i;}
};
static inline bool s_split(const int m, const int n, const int k, const int cutoff) {return m>cutoff && n>cutoff && k>cutoff;}
//workspace needed for all levels below (m,n,k)
static size_t s_work(const int m, const int n, const int k, const int cutoff)
{
if(!s_split(m,n,k,cutoff)) return 0;
size_t mh=m/2, nh=n/2, kh=k/2;
return (mh*kh>mh*nh ? mh*kh : mh*nh) + kh*nh + s_work(mh,nh,kh,cutoff);
}
//C(m x n, ldc) = alpha op(A) op(B)
template<typename T>
static void s_rec(const int m, const int n, const int k, const T alpha, const s_op<T> &A, const s_op<T> &B, T *C, const int ldc, T *ws, const int cutoff)
{
if(!s_split(m,n,k,cutoff)) {s_gemm(A.t,B.t,m,n,k,alpha,A.p,A.ld,B.p,B.ld,(T)0,C,ldc); return;}
const int mh=m/2, nh=n/2, kh=k/2;
//stored dimensions of the quadrants of A and B
const int ar= A.t=='n'?mh:kh, ac= A.t=='n'?kh:mh;
const int br= B.t=='n'?kh:nh, bc= B.t=='n'?nh:kh;
const T *A11=A.at(0,0), *A12=A.at(0,kh), *A21=A.at(mh,0), *A22=A.at(mh,kh);
const T *B11=B.at(0,0), *B12=B.at(0,nh), *B21=B.at(kh,0), *B22=B.at(kh,nh);
T *C11=C, *C12=C+nh, *C21=C+(size_t)mh*ldc, *C22=C21+nh;
const size_t xs= (size_t)mh*kh>(size_t)mh*nh ? (size_t)mh*kh : (size_t)mh*nh;
T *X=ws, *Y=ws+xs, *wn=Y+(size_t)kh*nh;
const s_op<T> sx={X,ac,A.t}, sy={Y,bc,B.t}; //X, Y hold sums of quadrants of A, B in their stored layout, contiguous (ld = stored columns), same flag
const s_op<T> a11={A11,A.ld,A.t}, a12={A12,A.ld,A.t}, a22={A22,A.ld,A.t};
const s_op<T> b11={B11,B.ld,B.t}, b21={B21,B.ld,B.t}, b22={B22,B.ld,B.t};
s_add(ar,ac,X,ac,A11,A.ld,A21,A.ld,true); //S3=A11-A21
s_add(br,bc,Y,bc,B22,B.ld,B12,B.ld,true); //T3=B22-B12
s_rec(mh,nh,kh,alpha,sx,sy,C21,ldc,wn,cutoff); //P7
s_add(ar,ac,X,ac,A21,A.ld,A22,A.ld,false); //S1=A21+A22
s_add(br,bc,Y,bc,B12,B.ld,B11,B.ld,true); //T1=B12-B11
s_rec(mh,nh,kh,alpha,sx,sy,C22,ldc,wn,cutoff); //P5
s_add(ar,ac,X,ac,X,ac,A11,A.ld,true); //S2=S1-A11
s_add(br,bc,Y,bc,B22,B.ld,Y,bc,true); //T2=B22-T1
s_rec(mh,nh,kh,alpha,sx,sy,C12,ldc,wn,cutoff); //P6
s_add(ar,ac,X,ac,A12,A.ld,X,ac,true); //S4=A12-S2
s_rec(mh,nh,kh,alpha,sx,b22,C11,ldc,wn,cutoff); //P3
s_rec(mh,nh,kh,alpha,a11,b11,X,nh,wn,cutoff); //P1 in X
s_add(mh,nh,C12,ldc,X,nh,C12,ldc,false); //U2=P1+P6
s_add(mh,nh,C21,ldc,C12,ldc,C21,ldc,false); //U3=U2+P7
s_add(mh,nh,C12,ldc,C12,ldc,C22,ldc,false); //U4=U2+P5
s_add(mh,nh,C22,ldc,C21,ldc,C22,ldc,false); //C22=U3+P5
s_add(mh,nh,C12,ldc,C12,ldc,C11,ldc,false); //C12=U4+P3
s_add(br,bc,Y,bc,Y,bc,B21,B.ld,true); //T4=T2-B21
s_rec(mh,nh,kh,alpha,a22,sy,C11,ldc,wn,cutoff); //P4
s_add(mh,nh,C21,ldc,C21,ldc,C11,ldc,true); //C21=U3-P4
s_rec(mh,nh,kh,alpha,a12,b21,C11,ldc,wn,cutoff); //P2
s_add(mh,nh,C11,ldc,X,nh,C11,ldc,false); //C11=P1+P2
//odd dimensions
const int me=2*mh, ne=2*nh, ke=2*kh;
if(k>ke) s_gemm(A.t,B.t,me,ne,1,alpha,A.at(0,ke),A.ld,B.at(ke,0),B.ld,(T)1,C,ldc);
if(n>ne) s_gemm(A.t,B.t,me,1,k,alpha,A.p,A.ld,B.at(0,ne),B.ld,(T)0,C+ne,ldc);
if(m>me) s_gemm(A.t,B.t,1,n,k,alpha,A.at(me,0),A.ld,B.p,B.ld,(T)0,C+(size_t)me*ldc,ldc);
}
//C = beta C + alpha op(A) op(B), row-major m x n
template<typename T>
static void s_top(const int m, const int n, const int k, const T alpha, const T *a, const int lda, const char ta, const T *b, const int ldb, const char tb, const T beta, T *c, const int cutoff)
{
const s_op<T> A={a,lda,ta}, B={b,ldb,tb};
if(!s_split(m,n,k,cutoff)) {s_gemm(ta,tb,m,n,k,alpha,a,lda,b,ldb,beta,c,n); return;}
const size_t w=s_work(m,n,k,cutoff);
const bool acc= beta!=(T)0;
T *ws=s_alloc<T>(w+(acc?(size_t)m*n:0));
T *cc= acc? ws+w : c;
s_rec(m,n,k,alpha,A,B,cc,n,ws,cutoff);
if(acc) for(size_t i=0; i<(size_t)m*n; ++i) c[i]=beta*c[i]+cc[i];
s_free(ws);
}
static inline char s_flag(const char t, const bool cmplx) {char tt=tolower(t); return tt=='n'?'n':(tt=='c'&&cmplx?'c':'t');}
template<typename T>
static void s_strassen(NRMat<T> &c, const T &beta, const NRMat<T> &a, const char transa, const NRMat<T> &b, const char transb, const T &alpha, const int cutoff)
{
const char ta=s_flag(transa,LA_traits<T>::is_complex()), tb=s_flag(transb,LA_traits<T>::is_complex());
int k(ta=='n'?a.ncols():a.nrows());
#ifdef DEBUG
int l(ta=='n'?a.nrows():a.ncols());
int kk(tb=='n'?b.nrows():b.ncols());
int ll(tb=='n'?b.ncols():b.nrows());
if (l!=c.nrows() || ll!=c.ncols() || k!=kk) laerror("incompatible matrices in NRMat<T>::strassen(...)");
if(b.ncols() <=0 || c.ncols()<=0) laerror("illegal matrix dimension in strassen");
#endif #endif
SAME_LOC3(c, a, b);
NOT_GPU(c);
if(cutoff<1) laerror("illegal cutoff in strassen");
if(alpha==(T)0 && beta==(T)1) return;
c.copyonwrite();
s_top(c.nrows(),c.ncols(),k,alpha,(const T *)a,a.ncols(),ta,(const T *)b,b.ncols(),tb,beta,(T *)c,cutoff);
}
#define INSTANTIZE(T) \
template<> \
void NRMat<T>::strassen(const T &beta, const NRMat<T> &a, const char transa, const NRMat<T> &b, const char transb, const T &alpha, const int cutoff) \
{s_strassen(*this,beta,a,transa,b,transb,alpha,cutoff);}
INSTANTIZE(double)
INSTANTIZE(float)
INSTANTIZE(std::complex<double>)
INSTANTIZE(std::complex<float>)
}//namespace
+4 -13
View File
@@ -324,16 +324,11 @@ cout <<" big1*big2 "<<big3[0][0]<<" time "<<clock()/((double) (CLOCKS_PER_SEC))-
*/ */
#ifndef NO_STRASSEN
if(0) if(0)
{ {
NRMat<double> atest, btest,ctest; NRMat<double> atest, btest,ctest;
{ int cc;
int cc,c1,c2,c3; cin >>cc;
cin >>cc>>c1>>c2>>c3;
atest.s_cutoff(cc,c1,c2,c3);
}
cin>>atest; cin>>atest;
cin>>btest; cin>>btest;
@@ -342,7 +337,7 @@ dtest.gemm(0., atest, 't', btest, 'n', 1.);
cout << dtest; cout << dtest;
NRMat<double> etest(atest.nrows(),btest.ncols()); NRMat<double> etest(atest.nrows(),btest.ncols());
etest.strassen(0., atest, 't', btest, 'n', 1.); etest.strassen(0., atest, 't', btest, 'n', 1., cc);
cout << etest; cout << etest;
} }
@@ -363,13 +358,11 @@ cout <<" classical big1*big2 "<<big3[0][0]<<" time "<<clock()/((double) (CLOCKS_
for (int c=64; c<=512;c+=64) for (int c=64; c<=512;c+=64)
{ {
big4.s_cutoff(c,c,c,c);
t=clock()/((double) (CLOCKS_PER_SEC)); t=clock()/((double) (CLOCKS_PER_SEC));
big4.strassen(0., big1, 'n', big2, 'n', 1.); big4.strassen(0., big1, 'n', big2, 'n', 1., c);
cout <<"cutoff "<<c<<" big1*big2 "<<big4[0][0]<<" time "<<clock()/((double) (CLOCKS_PER_SEC))-t <<"\n"; cout <<"cutoff "<<c<<" big1*big2 "<<big4[0][0]<<" time "<<clock()/((double) (CLOCKS_PER_SEC))-t <<"\n";
} }
} }
#endif
if(0) if(0)
{ {
@@ -1887,7 +1880,6 @@ c.moveto(gpu1);
cout << "Error = "<<(c-cgpu).norm()<<endl; cout << "Error = "<<(c-cgpu).norm()<<endl;
} }
/*
if(0) if(0)
{ {
CSRMat<double> h0; CSRMat<double> h0;
@@ -1910,7 +1902,6 @@ NRMat<complex<double> >r2 = exp(h2);
cout <<"error = "<<(r2-NRMat<complex<double> >(r)).norm()<<endl; cout <<"error = "<<(r2-NRMat<complex<double> >(r)).norm()<<endl;
} }
} }
*/
if(0) if(0)
+29 -3
View File
@@ -1187,6 +1187,16 @@ std::cout<<"cblas_dgemm CPU time " << (clock()-t0)/(double)CLOCKS_PER_SEC<<std::
#endif #endif
} }
template<>
void auxmatmult<float>(int nn, int mm, int kk, float *r, const float *a, const float *b, float alpha, float beta, bool conjugate)
{
clock_t t0=clock();
cblas_sgemm(CblasRowMajor, CblasNoTrans, CblasTrans, nn, mm, kk, alpha, a, kk, b, kk, beta, r, mm);
#ifdef TIMING
std::cout<<"cblas_sgemm CPU time " << (clock()-t0)/(double)CLOCKS_PER_SEC<<std::endl;
#endif
}
template<> template<>
void auxmatmult<std::complex<double> >(int nn, int mm, int kk, std::complex<double> *r, const std::complex<double> *a, const std::complex<double> *b, std::complex<double> alpha, std::complex<double> beta, bool conjugate) void auxmatmult<std::complex<double> >(int nn, int mm, int kk, std::complex<double> *r, const std::complex<double> *a, const std::complex<double> *b, std::complex<double> alpha, std::complex<double> beta, bool conjugate)
{ {
@@ -1197,6 +1207,16 @@ std::cout<<"cblas_zgemm CPU time " << (clock()-t0)/(double)CLOCKS_PER_SEC<<std::
#endif #endif
} }
template<>
void auxmatmult<std::complex<float> >(int nn, int mm, int kk, std::complex<float> *r, const std::complex<float> *a, const std::complex<float> *b, std::complex<float> alpha, std::complex<float> beta, bool conjugate)
{
clock_t t0=clock();
cblas_cgemm(CblasRowMajor, CblasNoTrans, (conjugate?CblasConjTrans:CblasTrans), nn, mm, kk, &alpha, a, kk, b, kk, &beta, r, mm);
#ifdef TIMING
std::cout<<"cblas_cgemm CPU time " << (clock()-t0)/(double)CLOCKS_PER_SEC<<std::endl;
#endif
}
template<typename T> template<typename T>
@@ -1809,7 +1829,7 @@ for(int i=0; i<r; ++i)
umnew=u.submatrix(0,umnr-1,0,preserve-1); umnew=u.submatrix(0,umnr-1,0,preserve-1);
} }
else umnew=u; else umnew=u;
ret[r-i-1]=vt.transpose(true); ret[r-i-1]=vt.transpose(false);
umnew.diagmultr(w); umnew.diagmultr(w);
//rebuild tensor of the preserved shape from matrix //rebuild tensor of the preserved shape from matrix
ushape.copyonwrite(); ushape.copyonwrite();
@@ -1825,7 +1845,7 @@ if(!is_flat()) laerror("this should not happen");
if(inverseorder) if(inverseorder)
{ {
NRPerm<int> p(rank()); p.identity(); NRPerm<int> p(rank()); p.identity();
names=names_saved.permuted(p.reverse()); if(names_saved.size()) names=names_saved.permuted(p.reverse());
} }
else else
names=names_saved; names=names_saved;
@@ -1869,7 +1889,7 @@ for(int i=rank()-1; i>=0; --i)
if(inverseorder) if(inverseorder)
{ {
NRPerm<int> p(rank()); p.identity(); NRPerm<int> p(rank()); p.identity();
r.names=names.permuted(p.reverse()); if(is_named()) r.names=names.permuted(p.reverse());
} }
else else
r.names=names; r.names=names;
@@ -2646,6 +2666,12 @@ template std::ostream & operator<<(std::ostream &s, const Tensor<double> &x);
template std::ostream & operator<<(std::ostream &s, const Tensor<std::complex<double> > &x); template std::ostream & operator<<(std::ostream &s, const Tensor<std::complex<double> > &x);
template std::istream & operator>>(std::istream &s, Tensor<double> &x); template std::istream & operator>>(std::istream &s, Tensor<double> &x);
template std::istream & operator>>(std::istream &s, Tensor<std::complex<double> > &x); template std::istream & operator>>(std::istream &s, Tensor<std::complex<double> > &x);
template class Tensor<float>;
template class Tensor<std::complex<float> >;
template std::ostream & operator<<(std::ostream &s, const Tensor<float> &x);
template std::ostream & operator<<(std::ostream &s, const Tensor<std::complex<float> > &x);
template std::istream & operator>>(std::istream &s, Tensor<float> &x);
template std::istream & operator>>(std::istream &s, Tensor<std::complex<float> > &x);
}//namespace }//namespace
+1 -1
View File
@@ -446,7 +446,7 @@ public:
Tensor merge_indices(const INDEXLIST &il, int symmetry=0) const; //opposite to flatten (merging with optional symmetrization/antisymmetrization and compression) Tensor merge_indices(const INDEXLIST &il, int symmetry=0) const; //opposite to flatten (merging with optional symmetrization/antisymmetrization and compression)
Tensor merge_indices(const NRVec<INDEXNAME> &nl, int symmetry=0) const {return merge_indices(findindexlist(nl),symmetry);}; Tensor merge_indices(const NRVec<INDEXNAME> &nl, int symmetry=0) const {return merge_indices(findindexlist(nl),symmetry);};
NRVec<NRMat<T> > Tucker(typename LA_traits<T>::normtype thr=1e-12, bool inverseorder=false); //HOSVD-Tucker decomposition, return core tensor in *this, flattened NRVec<NRMat<T> > Tucker(typename LA_traits<T>::normtype thr=LA_precision<typename LA_traits<T>::normtype>::svd_threshold(), bool inverseorder=false); //HOSVD-Tucker decomposition, return core tensor in *this, flattened
Tensor inverseTucker(const NRVec<NRMat<T> > &x, bool inverseorder=false) const; //rebuild the original tensor from Tucker Tensor inverseTucker(const NRVec<NRMat<T> > &x, bool inverseorder=false) const; //rebuild the original tensor from Tucker
Tensor linear_transform(const NRVec<NRMat<T> > &x) const; //linear transform by a different matrix per each index group, preserving group symmetries Tensor linear_transform(const NRVec<NRMat<T> > &x) const; //linear transform by a different matrix per each index group, preserving group symmetries
Tensor linear_transform(const int g, const Tensor<T> &x) const; //linear transform a single group, preserve its position and symmetry, x must be rank-2 flat tensor (this is useful for raising/lowering indices) Tensor linear_transform(const int g, const Tensor<T> &x) const; //linear transform a single group, preserve its position and symmetry, x must be rank-2 flat tensor (this is useful for raising/lowering indices)
+72
View File
@@ -936,6 +936,77 @@ check("NRSMat::storesubmatrix(selection) copy-on-write and size check",unchanged
} }
//single precision: every method with the float defaults (opt_precision<float>) on float test problems
template<class O, class H>
static float runf(O &o, const OptProblem<float> *p, NRVec<float> &x, H &h, bool *converged)
{
h.my_verbose=0;
x.copyonwrite();
optresult<float> r=o.minimize(p,x,&h,NULL);
*converged=r.converged;
return r.f;
}
static NRVec<float> startpointf(const int n, const int seed)
{
NRVec<double> xd=startpoint(n,seed);
NRVec<float> x(n);
for(int i=0; i<n; ++i) x[i]=xd[i];
return x;
}
//the same method in double and in float from the same start, the float run with its default thresholds
template<template<class> class O, template<class> class H>
static void compare_float(const char *name, const OptProblem<double> *pd, const OptProblem<float> *pf, const int n, const int seed, const int maxstep, void (*modify)(H<double> &, H<float> &)=NULL)
{
NRVec<double> xd=startpoint(n,seed); NRVec<float> xf(n); for(int i=0; i<n; ++i) xf[i]=xd[i];
H<double> hd; H<float> hf; hd.my_maxstep=hf.my_maxstep=maxstep; if(modify) modify(hd,hf);
O<double> od; O<float> of;
bool convd, convf;
hd.my_verbose=0; xd.copyonwrite(); optresult<double> rd=od.minimize(pd,xd,&hd,NULL); convd=rd.converged;
float ff=runf(of,pf,xf,hf,&convf);
string s=string("float ")+name+" agrees with double";
check(s.c_str(),convd && convf && abs(ff-rd.f)<1e-5*max(1.,abs(rd.f)),ff-rd.f);
}
static void dfp(BFGS_hyperparameters<double> &hd, BFGS_hyperparameters<float> &hf) {hd.my_update=hf.my_update=QN_DFP; hd.my_c2=hf.my_c2=0.1;}
static void sr1(BFGS_hyperparameters<double> &hd, BFGS_hyperparameters<float> &hf) {hd.my_update=hf.my_update=QN_SR1;}
static void test_float()
{
cout <<"--- single precision (float)\n";
testproblem<float> tp; //f=0 at x=0, used for the derivative-free methods; its large constant term n(n-1)/2 cancels in f, so f is not
//resolved below about 3e-6 in float and gradient methods cannot reach g_thr there: they use testproblem_vqe
testproblem_vqe<double> vd; testproblem_vqe<float> vf;
compare_float<BFGS_Optimizer,BFGS_hyperparameters>("BFGS on testproblem_vqe",&vd,&vf,8,3,5000);
compare_float<BFGS_Optimizer,BFGS_hyperparameters>("DFP on testproblem_vqe",&vd,&vf,8,3,5000,&dfp);
compare_float<BFGS_Optimizer,BFGS_hyperparameters>("SR1 on testproblem_vqe",&vd,&vf,8,3,5000,&sr1);
compare_float<LBFGS_Optimizer,LBFGS_hyperparameters>("L-BFGS on testproblem_vqe",&vd,&vf,8,3,5000);
compare_float<CG_Optimizer,CG_hyperparameters>("CG on testproblem_vqe",&vd,&vf,8,3,5000);
compare_float<GDIIS_Optimizer,GDIIS_hyperparameters>("GDIIS on testproblem_vqe",&vd,&vf,8,3,5000);
{NelderMead_hyperparameters<float> h; h.my_maxstep=100000; NelderMead_Optimizer<float> o; NRVec<float> x=startpointf(4,1); bool conv; float f=runf(o,&tp,x,h,&conv); check("float Nelder-Mead converges on testproblem n=4 (default f_thr, x_thr)",conv && f<1e-5,f);}
{Powell_hyperparameters<float> h; Powell_Optimizer<float> o; NRVec<float> x=startpointf(10,1); bool conv; float f=runf(o,&tp,x,h,&conv); check("float Powell converges on testproblem n=10 (default f_thr, x_thr)",conv && f<1e-5,f);}
{Cobyla_hyperparameters<float> h; Cobyla_Optimizer<float> o; NRVec<float> x=startpointf(4,1); bool conv; float f=runf(o,&tp,x,h,&conv); check("float COBYLA converges on testproblem n=4 (default rhoend)",conv && f<1e-5,f);}
{Adam_hyperparameters<float> h; h.my_maxstep=20000; h.my_lr=1e-2; Adam_Optimizer<float> o; NRVec<float> x=startpointf(10,1); bool conv; float f=runf(o,&tp,x,h,&conv); check("float Adam converges on testproblem n=10",conv && f<1e-5,f);}
{//finite-difference gradient with the float default step
FiniteDifference<float> fd(&tp); NRVec<float> x=startpointf(10,2), g(10), gfd(10); tp.funcgrad(x,&g); fd.funcgrad(x,&gfd); gfd-=g;
check("float FiniteDifference gradient (default step 5e-3)",gfd.norm()<1e-3*g.norm(),gfd.norm()/g.norm());
FiniteDifference<double> fdd(&vd); FiniteDifference<float> fdf(&vf);
compare_float<BFGS_Optimizer,BFGS_hyperparameters>("BFGS with finite-difference gradients (default steps)",&fdd,&fdf,8,3,5000);
}
{//Hessian-based methods on the VQE-like test problem, compared with double
NRVec<double> xd=startpoint(6,3); NRVec<float> xf(6); for(int i=0;i<6;++i) xf[i]=xd[i];
TrustRegion_hyperparameters<double> hd; TrustRegion_Optimizer<double> od; double fd=run(od,&vd,xd,hd);
TrustRegion_hyperparameters<float> hf; TrustRegion_Optimizer<float> of; bool conv; float ff=runf(of,&vf,xf,hf,&conv);
check("float trust region on testproblem_vqe agrees with double",conv && abs(ff-fd)<1e-5*max(1.,abs(fd)),ff-fd);
}
{//least squares: NIST Misra1a in float needs Marquardt scaling (unscaled b1 steps fall below float resolution)
testproblem_misra1a<float> p; NRVec<float> b(2); for(int i=0;i<2;++i) b[i]=p.start(0,i);
LM_hyperparameters<float> h; h.my_maxstep=100000; h.my_scaling=true; LM_Optimizer<float> o; bool conv; float f=runf(o,&p,b,h,&conv);
float perr=0; for(int i=0;i<2;++i) perr=max(perr,(float)abs(b[i]-p.certified(i))/abs(p.certified(i)));
check("float Levenberg-Marquardt fits NIST Misra1a",conv && perr<1e-3,perr);
}
}
int main(int argc, char **argv) int main(int argc, char **argv)
{ {
test_problems_and_capabilities(); test_problems_and_capabilities();
@@ -949,6 +1020,7 @@ test_leastsquares();
test_maxfunc_and_parameters(); test_maxfunc_and_parameters();
test_diis(); test_diis();
test_copyonwrite(); test_copyonwrite();
test_float();
cout <<"=== "<<npass<<" passed, "<<nfail<<" failed\n"; cout <<"=== "<<npass<<" passed, "<<nfail<<" failed\n";
return nfail; return nfail;
} }
+548 -2
View File
@@ -106,6 +106,23 @@ const NRVec<double> NRVec<double>::operator-() const {
return result; return result;
} }
template<>
const NRVec<float> NRVec<float>::operator-() const {
NRVec<float> result(*this);
result.copyonwrite();
#ifdef CUDALA
if(location == cpu){
#endif
cblas_sscal(nn, -1.0, result.v, 1);
#ifdef CUDALA
}else{
cublasSscal(nn, -1.0, result.v, 1);
TEST_CUBLAS("cublasSscal");
}
#endif
return result;
}
/***************************************************************************//** /***************************************************************************//**
@@ -129,6 +146,23 @@ const NRVec<std::complex<double> > NRVec<std::complex<double> >::operator-() con
return result; return result;
} }
template<>
const NRVec<std::complex<float> > NRVec<std::complex<float> >::operator-() const {
NRVec<std::complex<float> > result(*this);
result.copyonwrite();
#ifdef CUDALA
if(location == cpu){
#endif
cblas_csscal(nn, -1.0, result.v, 1);
#ifdef CUDALA
}else{
cublasCsscal(nn, -1.0, (cuComplex*)result.v, 1);
TEST_CUBLAS("cublasCsscal");
}
#endif
return result;
}
/***************************************************************************//** /***************************************************************************//**
* unary minus operator for vector of general type * unary minus operator for vector of general type
* @return the modified vector * @return the modified vector
@@ -199,6 +233,16 @@ void NRVec<double>::randomize(const double &x){
} }
} }
template<>
void NRVec<float>::randomize(const float &x){
NOT_GPU(*this);
copyonwrite();
for(register int i=0; i<nn; ++i){
v[i] = x*RANDDOUBLESIGNED();
}
}
/***************************************************************************//** /***************************************************************************//**
* fill the complex vector with pseudorandom numbers generated using uniform distribution * fill the complex vector with pseudorandom numbers generated using uniform distribution
* the real and imaginary parts are generated independently * the real and imaginary parts are generated independently
@@ -217,6 +261,16 @@ void NRVec<std::complex<double> >::randomize(const double &x) {
} }
} }
template<>
void NRVec<std::complex<float> >::randomize(const float &x) {
NOT_GPU(*this);
copyonwrite();
for(register int i=0; i<nn; ++i){
v[i] = std::complex<float>(x*RANDDOUBLESIGNED(), x*RANDDOUBLESIGNED());
}
}
/***************************************************************************//** /***************************************************************************//**
* constructor creating complex vector from a real one * constructor creating complex vector from a real one
* @param[in] rhs the real vector being converted into the complex one * @param[in] rhs the real vector being converted into the complex one
@@ -252,6 +306,31 @@ NRVec<std::complex<double> >::NRVec(const NRVec<double> &rhs, bool imagpart): nn
#endif #endif
} }
template<>
NRVec<std::complex<float> >::NRVec(const NRVec<float> &rhs, bool imagpart): nn(rhs.size()){
count = new int;
*count = 1;
#ifdef CUDALA
location = rhs.getlocation();
if(location == cpu){
#endif
v = (std::complex<float>*)new std::complex<float>[nn];
memset(v, 0, nn*sizeof(std::complex<float>));
cblas_scopy(nn, &rhs[0], 1, ((float *)v) + (imagpart?1:0), 2);
#ifdef CUDALA
}else{
v = (std::complex<float>*) gpualloc(nn*sizeof(std::complex<float>));
cublasCscal(nn, CUZERO, (cuComplex*)v, 1);
TEST_CUBLAS("cublasCscal");
cublasScopy(nn, &rhs[0], 1, ((float *)v) + (imagpart?1:0), 2);
TEST_CUBLAS("cublasScopy");
}
#endif
}
/***************************************************************************//** /***************************************************************************//**
* perform the <b>axpy</b> operation on the current real vector \f$\vec{v}\f$, i.e. * perform the <b>axpy</b> operation on the current real vector \f$\vec{v}\f$, i.e.
* \f[ \vec{v} \leftarrow \vec{v} + \alpha\vec{x} \f] * \f[ \vec{v} \leftarrow \vec{v} + \alpha\vec{x} \f]
@@ -278,6 +357,26 @@ void NRVec<double>::axpy(const double alpha, const NRVec<double> &x) {
#endif #endif
} }
template<>
void NRVec<float>::axpy(const float alpha, const NRVec<float> &x) {
#ifdef DEBUG
if (nn != x.nn) laerror("incompatible vectors");
#endif
SAME_LOC(*this, x);
copyonwrite();
#ifdef CUDALA
if(location == cpu){
#endif
cblas_saxpy(nn, alpha, x.v, 1, v, 1);
#ifdef CUDALA
}else{
cublasSaxpy(nn, alpha, x.v, 1, v, 1);
TEST_CUBLAS("cublasSaxpy");
}
#endif
}
/***************************************************************************//** /***************************************************************************//**
* perform the <b>axpy</b> operation on the current complex vector \f$\vec{v}\f$, i.e. * perform the <b>axpy</b> operation on the current complex vector \f$\vec{v}\f$, i.e.
* \f[ \vec{v} \leftarrow \vec{v} + \alpha\vec{x} \f] * \f[ \vec{v} \leftarrow \vec{v} + \alpha\vec{x} \f]
@@ -306,6 +405,28 @@ void NRVec<std::complex<double> >::axpy(const std::complex<double> alpha, const
} }
template<>
void NRVec<std::complex<float> >::axpy(const std::complex<float> alpha, const NRVec<std::complex<float> > &x){
#ifdef DEBUG
if (nn != x.nn) laerror("incompatible vectors");
#endif
SAME_LOC(*this, x);
copyonwrite();
#ifdef CUDALA
if(location == cpu){
#endif
cblas_caxpy(nn, &alpha, x.v, 1, v, 1);
#ifdef CUDALA
}else{
const cuComplex _alpha = make_cuComplex(alpha.real(), alpha.imag());
cublasCaxpy(nn, _alpha, (cuComplex*)x.v, 1, (cuComplex*)v, 1);
TEST_CUBLAS("cublasCaxpy");
}
#endif
}
/***************************************************************************//** /***************************************************************************//**
* perform the <b>axpy</b> operation on the current real vector \f$\vec{v}\f$, i.e. * perform the <b>axpy</b> operation on the current real vector \f$\vec{v}\f$, i.e.
* \f[ \vec{v} \leftarrow \vec{v} + \alpha\vec{x} \f] * \f[ \vec{v} \leftarrow \vec{v} + \alpha\vec{x} \f]
@@ -321,6 +442,14 @@ void NRVec<double>::axpy(const double alpha, const double *x, const int stride){
cblas_daxpy(nn, alpha, x, stride, v, 1); cblas_daxpy(nn, alpha, x, stride, v, 1);
} }
template<>
void NRVec<float>::axpy(const float alpha, const float *x, const int stride){
NOT_GPU(*this);
copyonwrite();
cblas_saxpy(nn, alpha, x, stride, v, 1);
}
/***************************************************************************//** /***************************************************************************//**
* perform the <b>axpy</b> operation on the current complex vector \f$\vec{v}\f$, i.e. * perform the <b>axpy</b> operation on the current complex vector \f$\vec{v}\f$, i.e.
* \f[ \vec{v} \leftarrow \vec{v} + \alpha\vec{x} \f] * \f[ \vec{v} \leftarrow \vec{v} + \alpha\vec{x} \f]
@@ -336,6 +465,14 @@ void NRVec<std::complex<double> >::axpy(const std::complex<double> alpha, const
cblas_zaxpy(nn, &alpha, x, stride, v, 1); cblas_zaxpy(nn, &alpha, x, stride, v, 1);
} }
template<>
void NRVec<std::complex<float> >::axpy(const std::complex<float> alpha, const std::complex<float> *x, const int stride){
NOT_GPU(*this);
copyonwrite();
cblas_caxpy(nn, &alpha, x, stride, v, 1);
}
/***************************************************************************//** /***************************************************************************//**
* assign real scalar value to every element of the current vector * assign real scalar value to every element of the current vector
* @param[in] a scalar value to be assigned * @param[in] a scalar value to be assigned
@@ -357,6 +494,22 @@ copyonwrite();
return *this; return *this;
} }
template<>
NRVec<float>& NRVec<float>::operator=(const float &a){
copyonwrite();
#ifdef CUDALA
if(location == cpu){
#endif
cblas_scopy(nn, &a, 0, v, 1);
#ifdef CUDALA
}else{
smart_gpu_set(nn, (float)0, v);
}
#endif
return *this;
}
/***************************************************************************//** /***************************************************************************//**
* assign complex scalar value to every element of the current vector * assign complex scalar value to every element of the current vector
* @param[in] a scalar value to be assigned * @param[in] a scalar value to be assigned
@@ -378,6 +531,22 @@ copyonwrite();
return *this; return *this;
} }
template<>
NRVec<std::complex<float> >& NRVec<std::complex<float> >::operator=(const std::complex<float> &a){
copyonwrite();
#ifdef CUDALA
if(location == cpu){
#endif
cblas_ccopy(nn, &a, 0, v, 1);
#ifdef CUDALA
}else{
smart_gpu_set(nn, (std::complex<float>)0, v);
}
#endif
return *this;
}
/***************************************************************************//** /***************************************************************************//**
* assign scalar value to every element of the current vector of general type <code>T</code> * assign scalar value to every element of the current vector of general type <code>T</code>
* @param[in] a scalar value to be assigned * @param[in] a scalar value to be assigned
@@ -433,6 +602,38 @@ NRVec<double>& NRVec<double>::normalize(double *norm){
return *this; return *this;
} }
template<>
NRVec<float>& NRVec<float>::normalize(float *norm){
float tmp(0.0);
#ifdef CUDALA
if(location == cpu){
#endif
tmp = cblas_snrm2(nn, v, 1);
if(norm) *norm = tmp;
#ifdef DEBUG
if(!tmp) laerror("attempt to normalize zero vector");
#endif
copyonwrite();
tmp = 1.0 / tmp;
cblas_sscal(nn, tmp, v, 1);
#ifdef CUDALA
}else{
tmp = cublasSnrm2(nn, v, 1);
TEST_CUBLAS("cublasSnrm2");
if(norm) *norm = tmp;
#ifdef DEBUG
if(!tmp) laerror("attempt to normalize zero vector");
#endif
copyonwrite();
tmp = 1.0 / tmp;
cublasSscal(nn, tmp, v, 1);
TEST_CUBLAS("cublasSscal");
}
#endif
return *this;
}
/***************************************************************************//** /***************************************************************************//**
* normalize current complex vector (in the Euclidean norm) * normalize current complex vector (in the Euclidean norm)
* @param[in] norm if not NULL, the norm of this vector is stored into *norm * @param[in] norm if not NULL, the norm of this vector is stored into *norm
@@ -471,6 +672,39 @@ NRVec<std::complex<double> > & NRVec<std::complex<double> >::normalize(double *n
return *this; return *this;
} }
template<>
NRVec<std::complex<float> > & NRVec<std::complex<float> >::normalize(float *norm){
float tmp(0.0);
#ifdef CUDALA
if(location == cpu){
#endif
tmp = cblas_scnrm2(nn, v, 1);
if(norm) *norm = tmp;
#ifdef DEBUG
if(tmp == 0.0) laerror("attempt to normalize zero vector");
#endif
copyonwrite();
tmp = 1.0 / tmp;
cblas_csscal(nn, tmp, v, 1);
#ifdef CUDALA
}else{
tmp = cublasScnrm2(nn, (cuComplex*)v, 1);
TEST_CUBLAS("cublasScnrm2");
if(norm) *norm = tmp;
#ifdef DEBUG
if(tmp == 0.0) laerror("attempt to normalize zero vector");
#endif
copyonwrite();
tmp = 1.0 / tmp;
cublasCsscal(nn, tmp, (cuComplex*)v, 1);
TEST_CUBLAS("cublasCsscal");
}
#endif
return *this;
}
/***************************************************************************//** /***************************************************************************//**
* perform the \b gemv operation on this real vector \f$\vec{y}\f$, i.e. * perform the \b gemv operation on this real vector \f$\vec{y}\f$, i.e.
* \f[\vec{y}\leftarrow \alpha\operatorname{op}(A)\cdot\vec{x}+\beta\vec{y}\f] * \f[\vec{y}\leftarrow \alpha\operatorname{op}(A)\cdot\vec{x}+\beta\vec{y}\f]
@@ -503,6 +737,28 @@ void NRVec<double>::gemv(const double beta, const NRMat<double> &A,
#endif #endif
} }
template<>
void NRVec<float>::gemv(const float beta, const NRMat<float> &A,
const char trans, const float alpha, const NRVec &x) {
#ifdef DEBUG
if((tolower(trans) == 'n'?A.ncols():A.nrows()) != x.size()){ laerror("incompatible vectors"); }
if((tolower(trans) == 'n'?A.nrows():A.ncols()) != nn){ laerror("incompatible result vector in gemv"); }
#endif
SAME_LOC3(*this, x, A);
copyonwrite();
#ifdef CUDALA
if(location==cpu){
#endif
cblas_sgemv(CblasRowMajor, (tolower(trans)=='n' ? CblasNoTrans:CblasTrans), A.nrows(), A.ncols(), alpha, A, A.ncols(), x.v, 1, beta, v, 1);
#ifdef CUDALA
}else{
cublasSgemv((tolower(trans)=='n'?'T':'N'), A.ncols(), A.nrows(), alpha, A, A.ncols(), x.v, 1, beta, v, 1);
TEST_CUBLAS("cublasSgemv");
}
#endif
}
/***************************************************************************//** /***************************************************************************//**
* perform the \b gemv operation on this complex vector \f$\vec{y}\f$, i.e. * perform the \b gemv operation on this complex vector \f$\vec{y}\f$, i.e.
* \f[\vec{y}\leftarrow \alpha\operatorname{op}(A)\cdot\vec{x}+\beta\vec{y}\f] * \f[\vec{y}\leftarrow \alpha\operatorname{op}(A)\cdot\vec{x}+\beta\vec{y}\f]
@@ -541,6 +797,34 @@ void NRVec<std::complex<double> >::gemv(const double beta, const NRMat<double> &
#endif #endif
} }
template<>
void NRVec<std::complex<float> >::gemv(const float beta, const NRMat<float> &A,
const char trans, const float alpha, const NRVec<std::complex<float> > &x) {
#ifdef DEBUG
if ((tolower(trans) == 'n'?A.ncols():A.nrows()) != x.size()){ laerror("incompatible vectors"); }
if((tolower(trans) == 'n'?A.nrows():A.ncols()) != nn){ laerror("incompatible result vector in gemv"); }
#endif
SAME_LOC3(*this, x, A);
copyonwrite();
#ifdef CUDALA
if(location==cpu){
#endif
cblas_sgemv(CblasRowMajor, (tolower(trans)=='n'?CblasNoTrans:CblasTrans),
A.nrows(), A.ncols(), alpha, A, A.ncols(), (float *)x.v, 2, beta, (float *)v, 2);
cblas_sgemv(CblasRowMajor, (tolower(trans)=='n'?CblasNoTrans:CblasTrans),
A.nrows(), A.ncols(), alpha, A, A.ncols(), ((float *)x.v) + 1, 2, beta, ((float *)v)+1, 2);
#ifdef CUDALA
}else{
cublasSgemv((tolower(trans)=='n'?'T':'N'), A.ncols(), A.nrows(), alpha, A, A.ncols(), (float*)(x.v), 2, beta, (float *)v, 2);
TEST_CUBLAS("cublasSgemv");
cublasSgemv((tolower(trans)=='n'?'T':'N'), A.ncols(), A.nrows(), alpha, A, A.ncols(), ((float *)x.v) + 1, 2, beta, ((float *)v)+1, 2);
TEST_CUBLAS("cublasSgemv");
}
#endif
}
/***************************************************************************//** /***************************************************************************//**
* perform the \b gemv operation on this complex vector \f$\vec{y}\f$, i.e. * perform the \b gemv operation on this complex vector \f$\vec{y}\f$, i.e.
@@ -581,6 +865,35 @@ void NRVec<std::complex<double> >::gemv(const std::complex<double> beta,
#endif #endif
} }
template<>
void NRVec<std::complex<float> >::gemv(const std::complex<float> beta,
const NRMat<std::complex<float> > &A, const char trans,
const std::complex<float> alpha, const NRVec<std::complex<float> > &x) {
#ifdef DEBUG
if ((tolower(trans) == 'n'?A.ncols():A.nrows()) != x.size()){ laerror("incompatible vectors"); }
if((tolower(trans) == 'n'?A.nrows():A.ncols()) != nn){ laerror("incompatible result vector in gemv"); }
#endif
SAME_LOC3(*this, x, A);
copyonwrite();
#ifdef CUDALA
if(location == cpu){
#endif
cblas_cgemv(CblasRowMajor, (tolower(trans)=='n' ? CblasNoTrans : (tolower(trans)=='c'?CblasConjTrans:CblasTrans)),
A.nrows(), A.ncols(), &alpha, A, A.ncols(), x.v, 1, &beta, v, 1);
#ifdef CUDALA
}else{
if(tolower(trans)=='c') laerror("gemv with conjugate transposition not implemented on GPU");
const cuComplex _alpha = make_cuComplex(alpha.real(), alpha.imag());
const cuComplex _beta = make_cuComplex(beta.real(), beta.imag());
cublasCgemv((tolower(trans)=='n'?'T':'N'), A.ncols(), A.nrows(),
_alpha, (cuComplex*)(A[0]), A.ncols(), (cuComplex*)(x.v), 1, _beta, (cuComplex*)v, 1);
TEST_CUBLAS("cublasCgemv");
}
#endif
}
/***************************************************************************//** /***************************************************************************//**
* perform the \b gemv operation on this real vector \f$\vec{y}\f$, i.e. * perform the \b gemv operation on this real vector \f$\vec{y}\f$, i.e.
* \f[\vec{y}\leftarrow \alpha\operatorname{op}(A)\cdot\vec{x}+\beta\vec{y}\f] * \f[\vec{y}\leftarrow \alpha\operatorname{op}(A)\cdot\vec{x}+\beta\vec{y}\f]
@@ -613,6 +926,28 @@ void NRVec<double>::gemv(const double beta, const NRSMat<double> &A,
#endif #endif
} }
template<>
void NRVec<float>::gemv(const float beta, const NRSMat<float> &A,
const char trans, const float alpha, const NRVec &x) {
#ifdef DEBUG
if(A.ncols() != x.size()){ laerror("incompatible dimensions"); }
if(A.nrows() != nn){ laerror("incompatible result vector in gemv"); }
#endif
SAME_LOC3(*this, A, x);
copyonwrite();
#ifdef CUDALA
if(location==cpu){
#endif
cblas_sspmv(CblasRowMajor, CblasLower, A.ncols(), alpha, A, x.v, 1, beta, v, 1);
#ifdef CUDALA
}else{
cublasSspmv('U', A.ncols(), alpha, A, x.v, 1, beta, v, 1);
TEST_CUBLAS("cublasSspmv");
}
#endif
}
/***************************************************************************//** /***************************************************************************//**
* perform the \c gemv operation on this complex vector \f$\vec{y}\f$, i.e. * perform the \c gemv operation on this complex vector \f$\vec{y}\f$, i.e.
@@ -650,6 +985,32 @@ void NRVec<std::complex<double> >::gemv(const double beta, const NRSMat<double>
#endif #endif
} }
template<>
void NRVec<std::complex<float> >::gemv(const float beta, const NRSMat<float> &A,
const char trans, const float alpha, const NRVec<std::complex<float> > &x) {
#ifdef DEBUG
if(A.ncols() != x.size()){ laerror("incompatible dimensions"); }
if(A.nrows() != nn){ laerror("incompatible result vector in gemv"); }
#endif
SAME_LOC3(*this, A, x);
copyonwrite();
#ifdef CUDALA
if(location == cpu){
#endif
cblas_sspmv(CblasRowMajor, CblasLower, A.ncols(), alpha, A, (float *)x.v, 2, beta, (float *)v, 2);
cblas_sspmv(CblasRowMajor, CblasLower, A.ncols(), alpha, A, ((float *)x.v) + 1, 2, beta, ((float *)v) + 1, 2);
#ifdef CUDALA
}else{
cublasSspmv('U', A.ncols(), alpha, A, (float*)(x.v), 2, beta, (float*)v, 2);
TEST_CUBLAS("cublasSspmv");
cublasSspmv('U', A.ncols(), alpha, A, ((float*)(x.v)) + 1, 2, beta, ((float*)v) + 1, 2);
TEST_CUBLAS("cublasSspmv");
}
#endif
}
/***************************************************************************//** /***************************************************************************//**
* perform the \b gemv operation on this complex vector \f$\vec{y}\f$, i.e. * perform the \b gemv operation on this complex vector \f$\vec{y}\f$, i.e.
* \f[\vec{y}\leftarrow \alpha\operatorname{op}(A)\cdot\vec{x}+\beta\vec{y}\f] * \f[\vec{y}\leftarrow \alpha\operatorname{op}(A)\cdot\vec{x}+\beta\vec{y}\f]
@@ -689,6 +1050,35 @@ void NRVec<std::complex<double> >::gemv(const std::complex<double> beta,
#endif #endif
} }
template<>
void NRVec<std::complex<float> >::gemv(const std::complex<float> beta,
const NRSMat<std::complex<float> > &A, const char trans,
const std::complex<float> alpha, const NRVec<std::complex<float> > &x){
#ifdef DEBUG
if(A.ncols() != x.size()) laerror("incompatible dimensions");
if(A.nrows() != nn){ laerror("incompatible result vector in gemv"); }
#endif
SAME_LOC3(*this, A, x);
copyonwrite();
#ifdef CUDALA
if(location == cpu){
#endif
if(tolower(trans) == 't') //row-major lower packed H is column-major upper packed H^T
cblas_chpmv(CblasColMajor, CblasUpper, A.ncols(), &alpha, A, x.v, 1, &beta, v, 1);
else
cblas_chpmv(CblasRowMajor, CblasLower, A.ncols(), &alpha, A, x.v, 1, &beta, v, 1);
#ifdef CUDALA
}else{
const cuComplex _alpha = make_cuComplex(alpha.real(), alpha.imag());
const cuComplex _beta = make_cuComplex(beta.real(), beta.imag());
cublasChpmv('U', A.ncols(), _alpha, (cuComplex*)((const std::complex<float>*)A), (cuComplex*)(x.v), 1, _beta, (cuComplex*)(this->v), 1);
TEST_CUBLAS("cublasChpmv");
}
#endif
}
/***************************************************************************//** /***************************************************************************//**
* computes the outer product of this real vector \f$\vec{a}\f$ with given * computes the outer product of this real vector \f$\vec{a}\f$ with given
* real vector \f$\vec{b}\f$ and scales the resulting matrix with factor \f$\alpha\f$, i.e. * real vector \f$\vec{b}\f$ and scales the resulting matrix with factor \f$\alpha\f$, i.e.
@@ -716,6 +1106,24 @@ const NRMat<double> NRVec<double>::otimes(const NRVec<double> &b,const bool conj
return result; return result;
} }
template<>
const NRMat<float> NRVec<float>::otimes(const NRVec<float> &b,const bool conj, const float &scale) const {
SAME_LOC(*this, b);
NRMat<float> result(0.0, nn, b.nn, this->getlocation());
#ifdef CUDALA
if(location == cpu){
#endif
cblas_sger(CblasRowMajor, nn, b.nn, scale, v, 1, b.v, 1, result, b.nn);
#ifdef CUDALA
}else{
cublasSger(b.nn, nn, scale, b.v, 1, v, 1, result[0], b.nn);
TEST_CUBLAS("cublasSger");
}
#endif
return result;
}
template<> template<>
const NRVec<double> NRVec<double>::otimes2vec(const NRVec<double> &b,const bool conj, const double &scale) const { const NRVec<double> NRVec<double>::otimes2vec(const NRVec<double> &b,const bool conj, const double &scale) const {
@@ -734,6 +1142,24 @@ const NRVec<double> NRVec<double>::otimes2vec(const NRVec<double> &b,const bool
return result; return result;
} }
template<>
const NRVec<float> NRVec<float>::otimes2vec(const NRVec<float> &b,const bool conj, const float &scale) const {
SAME_LOC(*this, b);
NRVec<float> result(0.0, nn*b.nn, this->getlocation());
#ifdef CUDALA
if(location == cpu){
#endif
cblas_sger(CblasRowMajor, nn, b.nn, scale, v, 1, b.v, 1, result.v, b.nn);
#ifdef CUDALA
}else{
cublasSger(b.nn, nn, scale, b.v, 1, v, 1, result.v, b.nn);
TEST_CUBLAS("cublasSger");
}
#endif
return result;
}
/***************************************************************************//** /***************************************************************************//**
* computes the outer product of this complex vector \f$\vec{a}\f$ with given * computes the outer product of this complex vector \f$\vec{a}\f$ with given
* complex vector \f$\vec{b}\f$ and scales the resulting matrix with factor \f$\alpha\f$, i.e. * complex vector \f$\vec{b}\f$ and scales the resulting matrix with factor \f$\alpha\f$, i.e.
@@ -780,6 +1206,41 @@ NRVec<std::complex<double> >::otimes(const NRVec<std::complex<double> > &b, cons
return result; return result;
} }
template<>
const NRMat<std::complex<float> >
NRVec<std::complex<float> >::otimes(const NRVec<std::complex<float> > &b, const bool conj, const std::complex<float> &scale) const {
SAME_LOC(*this, b);
NRMat<std::complex<float> > result(0., nn, b.nn, this->getlocation());
#ifdef CUDALA
if(location == cpu){
#endif
if(conj){
cblas_cgerc(CblasRowMajor, nn, b.nn, &scale, v, 1, b.v, 1, result, b.nn);
}else{
cblas_cgeru(CblasRowMajor, nn, b.nn, &scale, v, 1, b.v, 1, result, b.nn);
}
#ifdef CUDALA
}else{
if(conj){
const cuComplex alpha = make_cuComplex(scale.real(), -scale.imag());
cublasCgerc(b.nn, nn, alpha, (cuComplex*)(b.v), 1, (cuComplex*)(v), 1, (cuComplex*)(result[0]), b.nn);
TEST_CUBLAS("cublasCgerc");
result.conjugateme();
}else{
const cuComplex alpha = make_cuComplex(scale.real(), +scale.imag());
cublasCgeru(b.nn, nn, alpha, (cuComplex*)(b.v), 1, (cuComplex*)(v), 1, (cuComplex*)(result[0]), b.nn);
TEST_CUBLAS("cublasCgeru");
}
}
#endif
return result;
}
template<> template<>
const NRVec<std::complex<double> > const NRVec<std::complex<double> >
NRVec<std::complex<double> >::otimes2vec(const NRVec<std::complex<double> > &b, const bool conj, const std::complex<double> &scale) const { NRVec<std::complex<double> >::otimes2vec(const NRVec<std::complex<double> > &b, const bool conj, const std::complex<double> &scale) const {
@@ -815,6 +1276,41 @@ NRVec<std::complex<double> >::otimes2vec(const NRVec<std::complex<double> > &b,
return result; return result;
} }
template<>
const NRVec<std::complex<float> >
NRVec<std::complex<float> >::otimes2vec(const NRVec<std::complex<float> > &b, const bool conj, const std::complex<float> &scale) const {
SAME_LOC(*this, b);
NRVec<std::complex<float> > result(0., nn*b.nn, this->getlocation());
#ifdef CUDALA
if(location == cpu){
#endif
if(conj){
cblas_cgerc(CblasRowMajor, nn, b.nn, &scale, v, 1, b.v, 1, result.v, b.nn);
}else{
cblas_cgeru(CblasRowMajor, nn, b.nn, &scale, v, 1, b.v, 1, result.v, b.nn);
}
#ifdef CUDALA
}else{
if(conj){
const cuComplex alpha = make_cuComplex(scale.real(), -scale.imag());
cublasCgerc(b.nn, nn, alpha, (cuComplex*)(b.v), 1, (cuComplex*)(v), 1, (cuComplex*)(result.v), b.nn);
TEST_CUBLAS("cublasCgerc");
result.conjugateme();
}else{
const cuComplex alpha = make_cuComplex(scale.real(), +scale.imag());
cublasCgeru(b.nn, nn, alpha, (cuComplex*)(b.v), 1, (cuComplex*)(v), 1, (cuComplex*)(result.v), b.nn);
TEST_CUBLAS("cublasCgeru");
}
}
#endif
return result;
}
template<> template<>
NRVec<std::complex<double> > complexify(const NRVec<double> &rhs) { NRVec<std::complex<double> > complexify(const NRVec<double> &rhs) {
@@ -834,6 +1330,24 @@ NRVec<std::complex<double> > complexify(const NRVec<double> &rhs) {
return r; return r;
} }
template<>
NRVec<std::complex<float> > complexify(const NRVec<float> &rhs) {
NRVec<std::complex<float> > r(rhs.size(), rhs.getlocation());
#ifdef CUDALA
if(rhs.getlocation() == cpu){
#endif
cblas_scopy(rhs.size(), &rhs[0], 1, (float *)(&r[0]), 2);
#ifdef CUDALA
}else{
r = 0;
cublasScopy(rhs.size(), rhs.v, 1, (float*)(r.v), 2);
TEST_CUBLAS("cublasScopy");
}
#endif
return r;
}
@@ -1073,6 +1587,22 @@ const double NRVec<double>::sum() const {
return result; return result;
} }
template <>
const float NRVec<float>::sum() const {
float result=0;
#ifdef CUDALA
if(location == cpu){
#endif
cblas_saxpy(nn, 1.0, v, 1, &result, 0);
#ifdef CUDALA
}else{
laerror("not implemented");
}
}
#endif
return result;
}
/***************************************************************************//** /***************************************************************************//**
* sum up the all of the current double-precision complex vector * sum up the all of the current double-precision complex vector
* @return sum * @return sum
@@ -1093,6 +1623,22 @@ const std::complex<double> NRVec<std::complex<double> >::sum() const {
return result; return result;
} }
template <>
const std::complex<float> NRVec<std::complex<float> >::sum() const {
std::complex<float> result=0;
#ifdef CUDALA
if(location == cpu){
#endif
cblas_caxpy(nn, &CONEF, v, 1, &result, 0);
#ifdef CUDALA
}else{
laerror("not implemented");
}
}
#endif
return result;
}
@@ -1232,8 +1778,8 @@ INSTANTIZE_CONCAT(std::complex<double>)
//template class NRVec<float>; template class NRVec<float>;
//template class NRVec<std::complex<float> >; template class NRVec<std::complex<float> >;
template class NRVec<double>; template class NRVec<double>;
template class NRVec<std::complex<double> >; template class NRVec<std::complex<double> >;
template class NRVec<char>; template class NRVec<char>;
+443
View File
@@ -55,6 +55,7 @@ template <> inline unsigned long long MYABS(const unsigned long long &x) {retur
* static constants used in several cblas-routines * static constants used in several cblas-routines
******************************************************************************/ ******************************************************************************/
const static std::complex<double> CONE = 1.0, CMONE = -1.0, CZERO = 0.0; const static std::complex<double> CONE = 1.0, CMONE = -1.0, CZERO = 0.0;
const static std::complex<float> CONEF = 1.0f, CMONEF = -1.0f, CZEROF = 0.0f;
#ifdef CUDALA #ifdef CUDALA
const static cuDoubleComplex CUONE = {1.,0.}, CUMONE = {-1.,0.}, CUZERO = {0.,0.}; const static cuDoubleComplex CUONE = {1.,0.}, CUMONE = {-1.,0.}, CUZERO = {0.,0.};
#endif #endif
@@ -336,6 +337,7 @@ public:
void gemv(const T beta, const NRMat<T> &a, const char trans, const T alpha, const NRVec &x); void gemv(const T beta, const NRMat<T> &a, const char trans, const T alpha, const NRVec &x);
void gemv(const T beta, const NRSMat<T> &a, const char trans /**< just for compatibility reasons */, const T alpha, const NRVec &x); void gemv(const T beta, const NRSMat<T> &a, const char trans /**< just for compatibility reasons */, const T alpha, const NRVec &x);
void gemv(const T beta, const SparseMat<T> &a, const char trans, const T alpha, const NRVec &x,const bool treat_as_symmetric = false); void gemv(const T beta, const SparseMat<T> &a, const char trans, const T alpha, const NRVec &x,const bool treat_as_symmetric = false);
void gemv(const T beta, const CSRMat<T> &a, const char trans, const T alpha, const NRVec &x);
void gemv( const typename LA_traits_complex<T>::Component_type beta, void gemv( const typename LA_traits_complex<T>::Component_type beta,
const typename LA_traits_complex<T>::NRMat_Noncomplex_type &a, const typename LA_traits_complex<T>::NRMat_Noncomplex_type &a,
@@ -375,6 +377,9 @@ public:
return result; return result;
}; };
//! multiply given compressed row sparse matrix with this vector from left (defined in csrmat.h)
inline const NRVec operator*(const CSRMat<T> &mat) const;
//! compute the outer product of two vectors //! compute the outer product of two vectors
const NRMat<T> otimes(const NRVec<T> &rhs, const bool conjugate = false, const T &scale = 1) const; const NRMat<T> otimes(const NRVec<T> &rhs, const bool conjugate = false, const T &scale = 1) const;
@@ -485,6 +490,7 @@ public:
//! constructor creating vector from sparse matrix //! constructor creating vector from sparse matrix
explicit NRVec(const SparseMat<T> &rhs); explicit NRVec(const SparseMat<T> &rhs);
explicit NRVec(const CSRMat<T> &rhs);
//! routine for compatibility with sparse types //! routine for compatibility with sparse types
inline void simplify() {}; inline void simplify() {};
@@ -1517,6 +1523,7 @@ NRVec<std::complex<T> > complexify(const NRVec<T> &rhs) {
return r; return r;
} }
template<> NRVec<std::complex<double> > complexify<double>(const NRVec<double> &rhs); template<> NRVec<std::complex<double> > complexify<double>(const NRVec<double> &rhs);
template<> NRVec<std::complex<float> > complexify<float>(const NRVec<float> &rhs);
/***************************************************************************//** /***************************************************************************//**
* routine for moving vector data between CPU and GPU memory * routine for moving vector data between CPU and GPU memory
@@ -1580,6 +1587,24 @@ inline NRVec<double>& NRVec<double>::operator+=(const double &a) {
return *this; return *this;
} }
template<>
inline NRVec<float>& NRVec<float>::operator+=(const float &a) {
copyonwrite();
#ifdef CUDALA
if(location == cpu){
#endif
cblas_saxpy(nn, 1.0, &a, 0, v, 1);
#ifdef CUDALA
}else{
float *d = gpuputdouble(a);
cublasSaxpy(nn, 1.0, d, 0, v, 1);
TEST_CUBLAS("cublasSaxpy");
gpufree(d);
}
#endif
return *this;
}
/***************************************************************************//** /***************************************************************************//**
* adds a complex scalar value \f$\alpha\f$ to all elements of this complex vector \f$\vec{x}\f$ * adds a complex scalar value \f$\alpha\f$ to all elements of this complex vector \f$\vec{x}\f$
* \f[\vec{x}_i\leftarrow\vec{x}_i+\alpha\f] * \f[\vec{x}_i\leftarrow\vec{x}_i+\alpha\f]
@@ -1604,6 +1629,24 @@ inline NRVec<std::complex<double> >& NRVec<std::complex<double> >::operator+=(co
return *this; return *this;
} }
template<>
inline NRVec<std::complex<float> >& NRVec<std::complex<float> >::operator+=(const std::complex<float> &a) {
copyonwrite();
#ifdef CUDALA
if(location == cpu){
#endif
cblas_caxpy(nn, &CONEF, &a, 0, v, 1);
#ifdef CUDALA
}else{
std::complex<float> *d = gpuputcomplex(a);
cublasCaxpy(nn, CUONE, (cuComplex *)d, 0, (cuComplex *)v, 1);
TEST_CUBLAS("cublasCaxpy");
gpufree(d);
}
#endif
return *this;
}
/***************************************************************************//** /***************************************************************************//**
* subtracts a real scalar value \f$\alpha\f$ from all elements of this real vector \f$\vec{x}\f$ * subtracts a real scalar value \f$\alpha\f$ from all elements of this real vector \f$\vec{x}\f$
* \f[\vec{x}_i\leftarrow\vec{x}_i-\alpha\f] * \f[\vec{x}_i\leftarrow\vec{x}_i-\alpha\f]
@@ -1628,6 +1671,24 @@ inline NRVec<double>& NRVec<double>::operator-=(const double &a) {
return *this; return *this;
} }
template<>
inline NRVec<float>& NRVec<float>::operator-=(const float &a) {
copyonwrite();
#ifdef CUDALA
if(location == cpu){
#endif
cblas_saxpy(nn, -1.0, &a, 0, v, 1);
#ifdef CUDALA
}else{
float *d = gpuputdouble(a);
cublasSaxpy(nn, -1.0, d, 0, v, 1);
TEST_CUBLAS("cublasSaxpy");
gpufree(d);
}
#endif
return *this;
}
/***************************************************************************//** /***************************************************************************//**
* subtracts a complex scalar value \f$\alpha\f$ from all elements of this complex vector \f$\vec{x}\f$ * subtracts a complex scalar value \f$\alpha\f$ from all elements of this complex vector \f$\vec{x}\f$
* \f[\vec{x}_i\leftarrow\vec{x}_i-\alpha\f] * \f[\vec{x}_i\leftarrow\vec{x}_i-\alpha\f]
@@ -1652,6 +1713,24 @@ inline NRVec<std::complex<double> >& NRVec<std::complex<double> >::operator-=(co
return *this; return *this;
} }
template<>
inline NRVec<std::complex<float> >& NRVec<std::complex<float> >::operator-=(const std::complex<float> &a) {
copyonwrite();
#ifdef CUDALA
if(location == cpu){
#endif
cblas_caxpy(nn, &CMONEF, &a, 0, v, 1);
#ifdef CUDALA
}else{
std::complex<float> *d = gpuputcomplex(a);
cublasCaxpy(nn, CUMONE, (cuComplex *)d, 0, (cuComplex *)v, 1);
TEST_CUBLAS("cublasCaxpy");
gpufree(d);
}
#endif
return *this;
}
/***************************************************************************//** /***************************************************************************//**
* adds a real vector \f$\vec{y}\f$ to this real vector \f$\vec{x}\f$ * adds a real vector \f$\vec{y}\f$ to this real vector \f$\vec{x}\f$
* \f[\vec{x}\leftarrow\vec{x}+\vec{y}\f] * \f[\vec{x}\leftarrow\vec{x}+\vec{y}\f]
@@ -1678,6 +1757,26 @@ inline NRVec<double>& NRVec<double>::operator+=(const NRVec<double> &rhs) {
return *this; return *this;
} }
template<>
inline NRVec<float>& NRVec<float>::operator+=(const NRVec<float> &rhs) {
#ifdef DEBUG
if (nn != rhs.nn) laerror("incompatible dimensions");
#endif
SAME_LOC(*this, rhs);
copyonwrite();
#ifdef CUDALA
if(location == cpu){
#endif
cblas_saxpy(nn, 1.0, rhs.v, 1, v, 1);
#ifdef CUDALA
}else{
cublasSaxpy(nn, 1.0, rhs.v, 1, v, 1);
TEST_CUBLAS("cubasDaxpy");
}
#endif
return *this;
}
/***************************************************************************//** /***************************************************************************//**
* adds a complex vector \f$\vec{y}\f$ to this complex vector \f$\vec{x}\f$ * adds a complex vector \f$\vec{y}\f$ to this complex vector \f$\vec{x}\f$
* \f[\vec{x}\leftarrow\vec{x}+\vec{y}\f] * \f[\vec{x}\leftarrow\vec{x}+\vec{y}\f]
@@ -1704,6 +1803,26 @@ inline NRVec<std::complex<double> >& NRVec<std::complex<double> >::operator+=(co
return *this; return *this;
} }
template<>
inline NRVec<std::complex<float> >& NRVec<std::complex<float> >::operator+=(const NRVec<std::complex<float> > &rhs) {
#ifdef DEBUG
if (nn != rhs.nn) laerror("incompatible dimensions");
#endif
SAME_LOC(*this, rhs);
copyonwrite();
#ifdef CUDALA
if(location == cpu){
#endif
cblas_caxpy(nn, &CONEF, rhs.v, 1, v, 1);
#ifdef CUDALA
}else{
cublasCaxpy(nn, CUONE, (cuComplex*)rhs.v, 1, (cuComplex*)v, 1);
TEST_CUBLAS("cublasCaxpy");
}
#endif
return *this;
}
/***************************************************************************//** /***************************************************************************//**
* subtracts a real vector \f$\vec{y}\f$ from this real vector \f$\vec{x}\f$ * subtracts a real vector \f$\vec{y}\f$ from this real vector \f$\vec{x}\f$
* \f[\vec{x}\leftarrow\vec{x}-\vec{y}\f] * \f[\vec{x}\leftarrow\vec{x}-\vec{y}\f]
@@ -1730,6 +1849,26 @@ inline NRVec<double> & NRVec<double>::operator-=(const NRVec<double> &rhs) {
return *this; return *this;
} }
template<>
inline NRVec<float> & NRVec<float>::operator-=(const NRVec<float> &rhs) {
#ifdef DEBUG
if (nn != rhs.nn) laerror("incompatible dimensions");
#endif
SAME_LOC(*this,rhs);
copyonwrite();
#ifdef CUDALA
if(location == cpu){
#endif
cblas_saxpy(nn, -1.0, rhs.v, 1, v, 1);
#ifdef CUDALA
}else{
cublasSaxpy(nn, -1.0, rhs.v, 1, v, 1);
TEST_CUBLAS("cubasDaxpy");
}
#endif
return *this;
}
/***************************************************************************//** /***************************************************************************//**
* subtracts a complex vector \f$\vec{y}\f$ from this complex vector \f$\vec{x}\f$ * subtracts a complex vector \f$\vec{y}\f$ from this complex vector \f$\vec{x}\f$
* \f[\vec{x}\leftarrow\vec{x}-\vec{y}\f] * \f[\vec{x}\leftarrow\vec{x}-\vec{y}\f]
@@ -1756,6 +1895,26 @@ inline NRVec<std::complex<double> >& NRVec<std::complex<double> >::operator-=(co
return *this; return *this;
} }
template<>
inline NRVec<std::complex<float> >& NRVec<std::complex<float> >::operator-=(const NRVec<std::complex<float> > &rhs) {
#ifdef DEBUG
if (nn != rhs.nn) laerror("incompatible dimensions");
#endif
SAME_LOC(*this, rhs);
copyonwrite();
#ifdef CUDALA
if(location == cpu){
#endif
cblas_caxpy(nn, &CMONEF, rhs.v, 1, v, 1);
#ifdef CUDALA
}else{
cublasCaxpy(nn, CUMONE, (cuComplex*)rhs.v, 1, (cuComplex*)v, 1);
TEST_CUBLAS("cublasCaxpy");
}
#endif
return *this;
}
/***************************************************************************//** /***************************************************************************//**
* multiplies this real vector \f$\vec{x}\f$ by a real scalar value \f$\alpha\f$ * multiplies this real vector \f$\vec{x}\f$ by a real scalar value \f$\alpha\f$
* \f[\vec{x}_i\leftarrow\alpha\vec{x}_i\f] * \f[\vec{x}_i\leftarrow\alpha\vec{x}_i\f]
@@ -1778,9 +1937,28 @@ inline NRVec<double>& NRVec<double>::operator*=(const double &a) {
return *this; return *this;
} }
template<>
inline NRVec<float>& NRVec<float>::operator*=(const float &a) {
copyonwrite();
#ifdef CUDALA
if(location == cpu){
#endif
cblas_sscal(nn, a, v, 1);
#ifdef CUDALA
}else{
cublasSscal(nn, a, v, 1);
TEST_CUBLAS("cublasSscal");
}
#endif
return *this;
}
template<> template<>
inline NRVec<double>& NRVec<double>::operator/=(const double &a) {return *this *= (1./a);} inline NRVec<double>& NRVec<double>::operator/=(const double &a) {return *this *= (1./a);}
template<>
inline NRVec<float>& NRVec<float>::operator/=(const float &a) {return *this *= (1.f/a);}
/***************************************************************************//** /***************************************************************************//**
* multiplies this complex vector \f$\vec{x}\f$ by a complex scalar value \f$\alpha\f$ * multiplies this complex vector \f$\vec{x}\f$ by a complex scalar value \f$\alpha\f$
* \f[\vec{x}_i\leftarrow\alpha\vec{x}_i\f] * \f[\vec{x}_i\leftarrow\alpha\vec{x}_i\f]
@@ -1804,9 +1982,29 @@ inline NRVec<std::complex<double> >& NRVec<std::complex<double> >::operator*=(co
return *this; return *this;
} }
template<>
inline NRVec<std::complex<float> >& NRVec<std::complex<float> >::operator*=(const std::complex<float> &a) {
copyonwrite();
#ifdef CUDALA
if(location == cpu){
#endif
cblas_cscal(nn, &a, v, 1);
#ifdef CUDALA
}else{
const cuComplex alpha = make_cuComplex(a.real(), a.imag());
cublasCscal(nn, alpha, (cuComplex*)v, 1);
TEST_CUBLAS("cublasCscal");
}
#endif
return *this;
}
template<> template<>
inline NRVec<std::complex<double> >& NRVec<std::complex<double> >::operator/=(const std::complex<double> &a) {return *this *= (1./a);} inline NRVec<std::complex<double> >& NRVec<std::complex<double> >::operator/=(const std::complex<double> &a) {return *this *= (1./a);}
template<>
inline NRVec<std::complex<float> >& NRVec<std::complex<float> >::operator/=(const std::complex<float> &a) {return *this *= (1.f/a);}
/***************************************************************************//** /***************************************************************************//**
* computes the inner product of this real vector \f$\vec{x}\f$ with given real vector \f$\vec{y]\f$ * computes the inner product of this real vector \f$\vec{x}\f$ with given real vector \f$\vec{y]\f$
* @param[in] rhs real vector \f$\vec{y}\f$ * @param[in] rhs real vector \f$\vec{y}\f$
@@ -1832,6 +2030,26 @@ inline const double NRVec<double>::operator*(const NRVec<double> &rhs) const {
return ret; return ret;
} }
template<>
inline const float NRVec<float>::operator*(const NRVec<float> &rhs) const {
float ret(0.0);
#ifdef DEBUG
if(nn != rhs.nn) laerror("incompatible dimensions");
#endif
SAME_LOC(*this, rhs);
#ifdef CUDALA
if(location == cpu){
#endif
ret = cblas_sdot(nn, v, 1, rhs.v, 1);
#ifdef CUDALA
}else{
ret = cublasSdot(nn, v, 1, rhs.v, 1);
TEST_CUBLAS("cublasSdot");
}
#endif
return ret;
}
/***************************************************************************//** /***************************************************************************//**
* computes the inner product of this complex vector \f$\vec{x}\f$ with given complex vector \f$\vec{y}\f$ * computes the inner product of this complex vector \f$\vec{x}\f$ with given complex vector \f$\vec{y}\f$
* taking conjugation of vector \f$\vec{x}\f$ into account * taking conjugation of vector \f$\vec{x}\f$ into account
@@ -1859,6 +2077,27 @@ inline const std::complex<double> NRVec<std::complex<double> >::operator*(const
return dot; return dot;
} }
template<>
inline const std::complex<float> NRVec<std::complex<float> >::operator*(const NRVec< std::complex<float> > &rhs) const {
#ifdef DEBUG
if(nn != rhs.nn) laerror("incompatible dimensions");
#endif
std::complex<float> dot;
SAME_LOC(*this, rhs);
#ifdef CUDALA
if(location == cpu){
#endif
cblas_cdotc_sub(nn, v, 1, rhs.v, 1, &dot);
#ifdef CUDALA
}else{
const cuComplex val = cublasCdotc(nn, (cuComplex*)v, 1, (cuComplex*)rhs.v, 1);
TEST_CUBLAS("cublasCdotc");
dot = std::complex<float>(cuCreal(val), cuCimag(val));
}
#endif
return dot;
}
/***************************************************************************//** /***************************************************************************//**
* computes the inner product of this real vector \f$\vec{x}\f$ with given real data * computes the inner product of this real vector \f$\vec{x}\f$ with given real data
* @param[in] y pointer to the double-precision real array (sufficient length assumed) * @param[in] y pointer to the double-precision real array (sufficient length assumed)
@@ -1871,6 +2110,12 @@ inline const double NRVec<double>::dot(const double *y, const int stride, bool c
return cblas_ddot(nn,v, 1,y,stride); return cblas_ddot(nn,v, 1,y,stride);
} }
template<>
inline const float NRVec<float>::dot(const float *y, const int stride, bool conjugate) const {
NOT_GPU(*this);
return cblas_sdot(nn,v, 1,y,stride);
}
/***************************************************************************//** /***************************************************************************//**
* computes the inner product of this complex vector \f$\vec{x}\f$ with given complex data * computes the inner product of this complex vector \f$\vec{x}\f$ with given complex data
* @param[in] y pointer to the double-precision complex array (sufficient length assumed) * @param[in] y pointer to the double-precision complex array (sufficient length assumed)
@@ -1886,6 +2131,15 @@ inline const std::complex<double> NRVec<std::complex<double> >::dot(const std::c
return dot; return dot;
} }
template<>
inline const std::complex<float> NRVec<std::complex<float> >::dot(const std::complex<float> *y, const int stride, bool conjugate) const {
std::complex<float> dot;
NOT_GPU(*this);
if(conjugate) cblas_cdotc_sub(nn, v,1,y, stride, &dot);
else cblas_cdotu_sub(nn, v,1,y, stride, &dot);
return dot;
}
/***************************************************************************//** /***************************************************************************//**
* computes the sum of the absolute values of the elements of this real vector \f$\vec{x}\f$ * computes the sum of the absolute values of the elements of this real vector \f$\vec{x}\f$
* @return \f$\sum_{i=1}^N\left|\vec{x}_i\right|\f$ * @return \f$\sum_{i=1}^N\left|\vec{x}_i\right|\f$
@@ -1906,6 +2160,22 @@ inline const double NRVec<double>::asum() const {
return ret; return ret;
} }
template<>
inline const float NRVec<float>::asum() const {
float ret(0.0);
#ifdef CUDALA
if(location == cpu){
#endif
ret = cblas_sasum(nn, v, 1);
#ifdef CUDALA
}else{
ret = cublasSasum(nn, v, 1);
TEST_CUBLAS("cublasSasum");
}
#endif
return ret;
}
/***************************************************************************//** /***************************************************************************//**
* for this complex vector \f$\vec{x}\f$ compute the expression * for this complex vector \f$\vec{x}\f$ compute the expression
@@ -1928,6 +2198,22 @@ inline const double NRVec<std::complex<double> >::asum() const {
return ret; return ret;
} }
template<>
inline const float NRVec<std::complex<float> >::asum() const {
float ret(0.0);
#ifdef CUDALA
if(location == cpu){
#endif
ret = cblas_scasum(nn, v, 1);
#ifdef CUDALA
}else{
ret = cublasScasum(nn, (cuComplex*)v, 1);
TEST_CUBLAS("cublasScasum");
}
#endif
return ret;
}
/***************************************************************************//** /***************************************************************************//**
* for this real vector \f$\vec{x}\f$ (of \f$N\f$ elements) determine the Frobenius norm * for this real vector \f$\vec{x}\f$ (of \f$N\f$ elements) determine the Frobenius norm
* @return \f$\sum_{i=1}^N\left|\vec{x}_i\right|^2\f$ * @return \f$\sum_{i=1}^N\left|\vec{x}_i\right|^2\f$
@@ -1948,6 +2234,22 @@ inline const double NRVec<double>::norm(int length, int offset, int stride) con
return ret; return ret;
} }
template<>
inline const float NRVec<float>::norm(int length, int offset, int stride) const {
float ret(0.);
#ifdef CUDALA
if(location == cpu){
#endif
ret = cblas_snrm2((length>=0?length:nn), v+offset, stride);
#ifdef CUDALA
}else{
ret = cublasSnrm2((length>=0?length:nn), v+offset, stride);
TEST_CUBLAS("cublasSnrm2");
}
#endif
return ret;
}
/***************************************************************************//** /***************************************************************************//**
* for this complex vector \f$\vec{x}\f$ (of \f$N\f$ elements) determine the Frobenius norm * for this complex vector \f$\vec{x}\f$ (of \f$N\f$ elements) determine the Frobenius norm
* @return \f$\sum_{i=1}^N\left|\vec{x}_i\right|^2\f$ * @return \f$\sum_{i=1}^N\left|\vec{x}_i\right|^2\f$
@@ -1968,6 +2270,22 @@ inline const double NRVec< std::complex<double> >::norm(int length, int offset,
return ret; return ret;
} }
template<>
inline const float NRVec< std::complex<float> >::norm(int length, int offset, int stride) const {
float ret(0.);
#ifdef CUDALA
if(location == cpu){
#endif
ret = cblas_scnrm2((length>=0?length:nn), v+offset, stride);
#ifdef CUDALA
}else{
ret = cublasScnrm2((length>=0?length:nn), ((cuComplex*)v)+offset, stride);
TEST_CUBLAS("cublasSzrm2");
}
#endif
return ret;
}
/***************************************************************************//** /***************************************************************************//**
* for this real vector \f$\vec{x}\f$ determine the element with largest absolute value * for this real vector \f$\vec{x}\f$ determine the element with largest absolute value
* @return \f$\vec{x}_i\f$ where \f$\left|\vec{x]_i\right|=\mathrm{max}_{j}\left|\vec{x}_{j}\right|\f$ * @return \f$\vec{x}_i\f$ where \f$\left|\vec{x]_i\right|=\mathrm{max}_{j}\left|\vec{x}_{j}\right|\f$
@@ -1991,6 +2309,25 @@ inline const double NRVec<double>::amax() const {
return ret; return ret;
} }
template<>
inline const float NRVec<float>::amax() const {
if(nn==0) laerror("amax of an empty vector");
float ret(0.0);
#ifdef CUDALA
if(location == cpu){
#endif
ret = v[cblas_isamax(nn, v, 1)];
#ifdef CUDALA
}else{
const int pozice = cublasIsamax(nn, v, 1) ;
TEST_CUBLAS("cublasIsamax");
gpuget(1, sizeof(float), v + pozice, &ret);
}
#endif
return ret;
}
template<> template<>
inline int NRVec<double>::iamax() const { inline int NRVec<double>::iamax() const {
if(nn==0) laerror("iamax of an empty vector"); if(nn==0) laerror("iamax of an empty vector");
@@ -2008,6 +2345,23 @@ inline int NRVec<double>::iamax() const {
return ret; return ret;
} }
template<>
inline int NRVec<float>::iamax() const {
if(nn==0) laerror("iamax of an empty vector");
int ret;
#ifdef CUDALA
if(location == cpu){
#endif
ret = cblas_isamax(nn, v, 1);
#ifdef CUDALA
}else{
ret = cublasIsamax(nn, v, 1) ;
TEST_CUBLAS("cublasIsamax");
}
#endif
return ret;
}
/***************************************************************************//** /***************************************************************************//**
* for this real vector \f$\vec{x}\f$ determine the element with smallest absolute value * for this real vector \f$\vec{x}\f$ determine the element with smallest absolute value
@@ -2039,6 +2393,32 @@ inline const double NRVec<double>::amin() const {
return ret; return ret;
} }
template<>
inline const float NRVec<float>::amin() const {
if(nn==0) laerror("amin of an empty vector");
float ret(0.0);
#ifdef CUDALA
if(location == cpu){
#endif
//BLAS routine idamin seems no to be supported
float val(0.0);
int index(0);
ret = std::abs(v[0]);
for(register int i = 1; i < nn; i++){
val = std::abs(v[i]);
if(val < ret){ index = i; ret = val; }
}
ret = v[index];
#ifdef CUDALA
}else{
const int pozice = cublasIsamin(nn, v, 1);
TEST_CUBLAS("cublasIsamin");
gpuget(1, sizeof(float), v + pozice, &ret);
}
#endif
return ret;
}
/***************************************************************************//** /***************************************************************************//**
* for a given complex vector \f$\vec{v}\f$, determine the smallest index of the maximum * for a given complex vector \f$\vec{v}\f$, determine the smallest index of the maximum
* magnitude element, i.e. maximal element in the 1-norm * magnitude element, i.e. maximal element in the 1-norm
@@ -2062,6 +2442,24 @@ inline const std::complex<double> NRVec<std::complex<double> >::amax() const {
return ret; return ret;
} }
template<>
inline const std::complex<float> NRVec<std::complex<float> >::amax() const {
if(nn==0) laerror("amax of an empty vector");
std::complex<float> ret(0., 0.);
#ifdef CUDALA
if(location == cpu){
#endif
ret = v[cblas_icamax(nn, v, 1) ];
#ifdef CUDALA
}else{
const int pozice = cublasIcamax(nn, (cuComplex*)v, 1);
TEST_CUBLAS("cublasIcamax");
gpuget(1, sizeof(std::complex<float>), v + pozice, &ret);
}
#endif
return ret;
}
template<> template<>
inline int NRVec<std::complex<double> >::iamax() const { inline int NRVec<std::complex<double> >::iamax() const {
if(nn==0) laerror("iamax of an empty vector"); if(nn==0) laerror("iamax of an empty vector");
@@ -2079,6 +2477,23 @@ inline int NRVec<std::complex<double> >::iamax() const {
return ret; return ret;
} }
template<>
inline int NRVec<std::complex<float> >::iamax() const {
if(nn==0) laerror("iamax of an empty vector");
int ret;
#ifdef CUDALA
if(location == cpu){
#endif
ret = cblas_icamax(nn, v, 1);
#ifdef CUDALA
}else{
ret = cublasIcamax(nn, (cuComplex*)v, 1);
TEST_CUBLAS("cublasIcamax");
}
#endif
return ret;
}
/***************************************************************************//** /***************************************************************************//**
* for a given complex vector \f$\vec{v}\f$, determine the smallest index of the minimum * for a given complex vector \f$\vec{v}\f$, determine the smallest index of the minimum
@@ -2113,6 +2528,34 @@ inline const std::complex<double> NRVec<std::complex<double> >::amin() const {
return ret; return ret;
} }
template<>
inline const std::complex<float> NRVec<std::complex<float> >::amin() const {
if(nn==0) laerror("amin of an empty vector");
std::complex<float> ret(0., 0.);
#ifdef CUDALA
if(location == cpu){
#endif
// izamin seems not to be supported
int index(0);
float val(0.0), min_val(std::numeric_limits<float>::max());
std::complex<float> z_val(0.0, 0.0);
for(register int i=0; i < nn; i++){
z_val = v[i];
val = std::abs(z_val.real()) + std::abs(z_val.imag());
if(val < min_val){ index = i; min_val = val; }
}
ret = v[index];
#ifdef CUDALA
}else{
const int pozice = cublasIcamin(nn, (cuComplex*)v, 1);
TEST_CUBLAS("cublasIcamin");
gpuget(1, sizeof(std::complex<float>), v + pozice, &ret);
}
#endif
return ret;
}
/***************************************************************************//** /***************************************************************************//**
* routine for raw output * routine for raw output