\chapter{Utilities}\label{ch:utilities} This chapter documents the auxiliary parts of \LA{} which are not linear algebra classes: a scratch-file storage of fixed-size records used by the out-of-core variants of the iterative solvers, the random number generators used by all \code{randomize()} functions, a few special functions and combinatorial numbers, small self-contained algorithms for embedded use, the reference of the error handling functions, the single include file \header{la.h} and the version string, elementary number theory and graph algorithms. The small classes Vec3/Mat3, Quaternion and bitvector are described in chapters~\ref{ch:vecmat3}, \ref{ch:quaternion} and \ref{ch:bitvector}. %===================================================================== \section{Scratch-file storage: AuxStorage}\label{sec:utilities:auxstorage} \laheader{auxstorage.h} \subsection{Purpose and storage} \code{AuxStorage} stores numbered records of equal length in a binary file, using unformatted \code{read}/\code{write} of the raw element array. It is used by \code{davidson}, \code{lanczos}, \code{gmres} and \code{DIIS} (chapter~\ref{ch:iterative}) to keep the subspace vectors on disk when the in-core option is not selected, and to write eigenvectors to a named file. The record length (in bytes) is fixed by the first \code{put()}; all later records must have the same size. Record \code{pos} starts at byte offset \code{pos*recl}, so records may be written in any order; reading a never written record lying before the end of the file gives zeros (a hole of the file), reading past the end of the file calls \code{laerror}. The class itself holds only a file descriptor and the file name; it is \emph{not} reference counted. The objects stored are ordinary \LA{} objects: \code{get()} calls \code{x.copyonwrite()} before reading into \code{x}, \code{put()} only reads its argument. The comment in the header gives the limits of the design: \begin{itemize} \item \code{T} must be plain data (\code{double}, \code{std::complex}, \dots); it will not work if \code{T} is a class with dynamically allocated components (e.g.\ \code{NRVec >} or \code{NRVec >}), since only the bytes of the top-level array are written; \item it cannot be implemented for \code{SparseMat}, which lacks a fixed record length; \item for more complex I/O the \code{put()} and \code{get()} methods of the individual classes are to be used (section~\ref{sec:concepts:io}). \end{itemize} \subsection{Reference} \begin{lareference} \lafunc{template class AuxStorage}{Scratch file of records whose elements are of type \code{T}.} \lafunc{AuxStorage(void)}{Creates and opens a new unique temporary file \header{AUX_XXXXXX} in \code{\$TMPDIR} if it is set, otherwise in the current working directory, by \code{mkostemp()} (atomic creation, with \code{O_LARGEFILE}), and unlinks it at once: the open descriptor keeps it usable, and its disk space is released when it is closed, also after a crash. \code{laerror} if the file cannot be created.} \lafunc{AuxStorage(const char *name)}{Opens the file \code{name} for reading and writing; an existing file of this name is first deleted (\code{unlink}), so the file always starts empty. The file is \emph{not} deleted by the destructor (used to keep eigenvectors). \code{laerror} if it cannot be opened or if the name has 32 or more characters.} \lafunc{~AuxStorage(void)}{Closes the file; deletes it if it was created by the default constructor.} \lafunc{AuxStorage(const AuxStorage &) = delete; AuxStorage &operator=(const AuxStorage &) = delete}{Not copyable (a copy would close the file twice); use pointers, as the iterative solvers do.} \lafunc{void put(const NRVec &x, const int pos)}{Writes the elements of \code{x} as record \code{pos} (counted from 0). The first \code{put} defines the record length; \code{laerror} if a later object has a different size in bytes.} \lafunc{void get(NRVec &x, const int pos) const}{Reads record \code{pos} into \code{x}, which must be already allocated with the right size. Calls \code{x.copyonwrite()}. \code{laerror} if nothing has been written yet, if the size of \code{x} in bytes differs from the record length, or if the record cannot be read in full.} \lafunc{void put(const NRMat &x, const int pos); void get(NRMat &x, const int pos) const}{The same for a dense matrix (record of \code{nrows()*ncols()} elements in the storage order of \code{NRMat}).} \lafunc{void put(const NRSMat &x, const int pos); void get(NRSMat &x, const int pos) const}{The same for a packed symmetric matrix (record of $n(n+1)/2$ elements).} \end{lareference} Only the byte size is compared, so a record written as a vector can be read into a matrix of the same number of elements. \subsection{Example} \begin{lstlisting} AuxStorage st; //temporary file AUX_xxxxxx in $TMPDIR or the working directory NRVec v(1.,100), w(100); st.put(v,3); //record 3, record length 100 doubles st.get(w,3); //w must be allocated with 100 elements \end{lstlisting} \subsection{Pitfalls} \begin{itemize} \item The target of \code{get()} must be preallocated with exactly the record size (otherwise \code{laerror}). \item The temporary file is placed in \code{\$TMPDIR}, or in the current working directory if \code{TMPDIR} is not set; it is not visible in the directory listing (unlinked), but occupies disk space there while the object exists. \item The file position is shared: \code{get()} and \code{put()} use \code{lseek64} followed by \code{read}/\code{write}, so one object must not be used concurrently from several threads. \item \code{lseek64}, \code{off64_t}, \code{O_LARGEFILE} and \code{mkostemp} are Linux (glibc) specific. \item A short write is not detected (only a negative return value of \code{write} is checked). \end{itemize} %===================================================================== \section{Random numbers}\label{sec:utilities:random} \laheader{la_random.h, la_random.cc} All random-number consumers of the library (\code{randomize()} of \code{NRVec}, \code{NRMat}, \code{NRSMat}, \code{Vec3}, \code{Mat3}, \code{Quaternion}, \code{bitvector}, random permutations, \dots) obtain random numbers through four macros, so that the generator can be replaced without changing the library. \header{la_random.h} is included by \header{la_traits.h}, i.e.\ by every \LA{} header. \begin{lareference} \lafunc{double randdouble()}{Uniform in $[0,1)$: \code{random()/(1.+RAND_MAX)}.} \lafunc{double randdoublesigned()}{Uniform in $[-1,1)$: \code{2.*random()/(1.+RAND_MAX)-1.}.} \lafunc{unsigned int randint32()}{32 random bits from two calls of \code{random()} (which gives 31 bits with glibc).} \lafunc{uint64_t randint64()}{64 random bits from three calls of \code{random()} ($31+31+2$ bits).} \lafunc{RANDDOUBLE, RANDDOUBLESIGNED, RANDINT32, RANDINT64}{Macros used at all call sites; unless defined before the inclusion of \header{la_random.h} they expand to \code{LA::randdouble}, \code{LA::randdoublesigned}, \code{LA::randint32}, \code{LA::randint64}.} \lafunc{WEAK_SYMBOL}{Expands to \code{__attribute__((weak))} with GNU compilers, empty otherwise.} \end{lareference} \subsection{Replacing the generator} The four functions are defined in \header{la_random.cc} as \emph{weak symbols}. A program can replace any of them by its own (strong) definition with the same signature in namespace \code{LA}; the linker then uses the user's function also in the calls made from inside the precompiled library, and the remaining default functions stay available: \begin{lstlisting} #include "la.h" namespace LA { double randdoublesigned() {return my_generator.uniform(-1.,1.);} //replaces the library's weak definition } \end{lstlisting} Defining one of the macros (e.g.\ \code{-DRANDDOUBLE=myrand}) affects only code compiled by the user (inline and template code in the headers), not the calls in the compiled library; the weak-symbol replacement is therefore the mechanism to use for a consistent change. \subsection{Seeding} The default functions use the C library generator \code{random()}, so they are seeded by \code{srandom(seed)}; with glibc \code{srand()} seeds the same generator (as used in \header{testoptimize.cc} and \header{t.cc}). Without seeding the sequence is the same in every run. A seed from the system can be taken e.g.\ from \header{/dev/random}, as in \header{t.cc}: \begin{lstlisting} int seed; int f=open("/dev/random",O_RDONLY); if(sizeof(int)!=read(f,&seed,sizeof(int))) laerror("cannot read /dev/random"); close(f); srand(seed); \end{lstlisting} The default generators are not thread safe in the sense of reproducibility (they share the global state of \code{random()}); a thread-safe or higher-quality generator is to be supplied by replacement. %===================================================================== \section{Special functions and combinatorial numbers}\label{sec:utilities:miscfunc} \laheader{miscfunc.h, miscfunc.cc} Plain functions in namespace \code{LA} for \code{double} and 64-bit unsigned integers. They keep internal static tables (filled on demand), so they are not thread safe on the first calls. \begin{lareference} \lafunc{const double c_euler}{Euler--Mascheroni constant $\gamma=0.5772156649\dots$} \lafunc{unsigned long long factorial(const int n)}{$n!$ from a table, $0\le n\le 20$; \code{laerror} for $n<0$ and for $n>20$ (overflow of 64 bits). Exact; used where integer arithmetic is needed (permutation ranks and counts, \code{binom}).} \lafunc{double double_factorial(const int n)}{$n!$ (not the double factorial $n!!$) in \code{double} precision, $0\le n\le 170$: \code{factorial(n)} for $n\le20$, beyond from a table extended on demand. \code{laerror} for $n<0$ and $n>170$. Used by \code{bernoulli_number}, \code{zeta} and \code{gamma}.} \lafunc{double factorialln(const int n)}{$\ln n!$ for any $n\ge0$: tabulated up to $n=30$, accumulated (and cached) up to $n=180$, Stirling series with Bernoulli-number corrections beyond. \code{laerror} for $n<0$.} \lafunc{unsigned long long binom(int n, int k)}{Binomial coefficient $\binom nk$; 0 for $k<0$ or $k>n$. With $k$ replaced by $\min(k,n-k)$, computed exactly by the recurrence $\binom{n-k+i}{i}=\binom{n-k+i-1}{i-1}(n-k+i)/i$, dividing by the common factor before each multiplication, so that no intermediate exceeds the result; cached for $n\le100$. \code{laerror} if the result does not fit into 64 bits (all $\binom nk$ fit for $n\le67$).} \lafunc{unsigned long long longpow(unsigned long long x, int i)}{$x^i$ by binary powering (modulo $2^{64}$, overflow not detected); returns 0 for $i<0$.} \lafunc{double zeta(double x)}{Riemann zeta function for real $x\ne1$. Integer arguments: exact values from Bernoulli numbers for $x\le0$ and $x=2,4,6,8$; otherwise (also for other integers): the defining sum for $x\ge9.1$, the Euler--Maclaurin summation formula with $N=10$ and ten Bernoulli terms for $-1.02\le x<9.1$ (including the neighbourhood of the pole, whose term $1/(x-1)$ is exact), and the reflection formula $\zeta(x)=2(2\pi)^{x-1}\sin(\pi x/2)\Gamma(1-x)\zeta(1-x)$ for $x<-1.02$. Relative accuracy about $10^{-13}$ or better (except close to the trivial zeros). \code{laerror} for $x=1$.} \lafunc{double gamma(double x)}{$\Gamma(x)$ for $x\le171$: $(x-1)!$ by \code{double_factorial} for integers, otherwise upward recursion to $x\ge8$ and the Stirling series. \code{laerror} for $x>171$ and for non-positive integers.} \lafunc{double gammln(double x)}{$\ln|\Gamma(x)|$, for integers via \code{factorialln}; \code{laerror} for non-positive integers.} \lafunc{double bernoulli_number(int n)}{Bernoulli number $B_n$ with $B_1=-\frac12$, $B_{2k+1}=0$ for $k\ge1$; $B_{2k}$ from a table up to $B_{10}$, otherwise from $\zeta(2k)$ by $B_{2k}=(-1)^{k+1}\,2\,(2k)!\,\zeta(2k)/(2\pi)^{2k}$ (cached). \code{laerror} for $n<0$.} \lafunc{unsigned long long simplicial(int d, int n)}{$d$-dimensional simplicial (figurate) number $s(d,n)=\binom{n+d-1}{d}$, i.e.\ the number of non-decreasing $d$-tuples of indices from $n$ values (used for the indexing of symmetric tensors, chapter~\ref{ch:tensor}). $s(d,n)=0$ for $n\le0$, $1$ for $d=0$ or $n=1$, $n$ for $d=1$. Computed by the recurrence $s(d,n)=s(d,n-1)+s(d-1,n)$ into a table growing on demand; \code{laerror} for $d\ge16$ or $n\ge1024$.} \lafunc{int inverse_simplicial(int d, unsigned long long s)}{Largest $n$ with \code{simplicial(d,n)}${}\le s$ (Newton iteration on the polynomial $s(d,n)$ followed by an integer correction); 0 for $s=0$. \code{laerror} for $d\ge16$.} \end{lareference} %===================================================================== \section{Simple algorithms for embedded use}\label{sec:utilities:simple} \laheader{simple.h, simple.cc} \header{simple.h} contains small algorithms on C arrays of compile-time size, independent of BLAS, LAPACK and of the rest of \LA{}. It is intended for embedded computers and can be compiled separately; it is installed with the library but \emph{not} included by \header{la.h}. Everything is in namespace \code{LA_Simple}. With \code{AVOID_STDSTREAM} defined, \header{iostream} is not included and the stream operator is omitted. \header{simple.cc} is currently empty. The element type \code{T} is a real floating-point type (\code{abs} and comparisons are used for pivoting). \begin{lareference} \lafunc{template T simple_gaussj(T (&a)[n][n], T (&b)[m][n])}{Gauss--Jordan elimination with full pivoting. On input \code{a} is the $n\times n$ matrix $A$ and \code{b} contains $m$ right-hand sides as its \emph{rows} \code{b[l][0..n-1]}. On return \code{a} contains $A^{-1}$, the rows of \code{b} the solutions, and the function returns $\det A$. For a singular matrix it returns 0 and \code{a}, \code{b} are left partially transformed. \code{T (&a)[n][n]} binds e.g.\ to \code{Mat3::elements()}.} \lafunc{template class simple_linfit}{Linear least-squares fit $y\approx\sum_{i=0}^{n-1}c_if_i(x)$ by accumulating the normal equations; the data points are not stored. Public members: \code{T fitmat[n][n]}, \code{T rhsmat[1][n]} (accumulated $\sum f_if_j$, $\sum f_iy$), \code{T fitcoef[n]} (result $c_i$), \code{int npoints}.} \lafunc{simple_linfit()}{Empty fit, coefficients zero.} \lafunc{void clear(bool keepresults=false)}{Discard the accumulated data (and the coefficients unless \code{keepresults}).} \lafunc{void input(const T (&funcs)[n], const T y)}{Add a data point given by the values $f_i(x)$ and $y$.} \lafunc{T solve(const T preserve=0)}{Solves the normal equations by \code{simple_gaussj}, stores the coefficients in \code{fitcoef} and returns the determinant of the normal matrix (0 if singular). If fewer than $n$ points have been input it returns 0 and leaves \code{fitcoef} unchanged. With \code{preserve==0} the accumulated data are cleared afterwards (the next fit starts from scratch); with \code{preserve!=0} they are kept and, if \code{preserve!=1}, multiplied by \code{preserve}, which gives an exponentially decaying weight of old points in a running fit (\code{npoints} is not reset then).} \lafunc{const T &operator[](int i) const}{Coefficient $c_i$.} \lafunc{std::ostream& operator<<(std::ostream &o, const simple_linfit &f)}{Prints the coefficients separated by spaces.} \end{lareference} Example (from \header{t.cc}), fit of a parabola: \begin{lstlisting} simple_linfit fit; double funcs[3]; for(int i=0; i<100; ++i) { double x = 10*RANDDOUBLESIGNED(); funcs[0]=1; funcs[1]=x; funcs[2]=x*x; fit.input(funcs, 2*x*x-3*x+1); } double det = fit.solve(); cout <<"det= "< NRIMat; typedef NRVec NRIVec; typedef NRMat NRDMat; typedef NRVec NRDVec; typedef NRMat > NRCMat; typedef NRVec > NRCVec; \end{lstlisting} Code which wants to avoid the global \code{using} directive can include the individual headers instead. %===================================================================== \section{Version information}\label{sec:utilities:version} The library contains the string \code{extern const char LA::version[]} (declared in \header{LA_version.h}, included by \header{la.h}). Its definition \header{version.cc} is regenerated at every \code{make} by the script \header{get_git_version} and contains the output of \code{git describe}, the HEAD commit hash and date, the compilation date, host and user name. A program can print it to record which library build produced its results: \begin{lstlisting} cout <<"LA version = "< : public std::list>}{List of (prime, exponent) pairs in increasing order.} \lafunc{FACTORIZATION factorization(const N &x)}{Prime factorization (empty for $x\le1$).} \lafunc{N nextprime(N x)}{Smallest prime $>x$ (2 for $x<2$).} \lafunc{std::ostream &operator<<(std::ostream &s, const FACTORIZATION &x)}{Prints \code{(p,e) (p,e) ...}.} \lafunc{N eulerphi(const FACTORIZATION &f); N eulerphi(const N &x)}{Euler's totient function.} \lafunc{N pow(const N &x, N i)}{$x^i$ by repeated squaring (overflow not checked).} \lafunc{N multmod(N x, N y, const N &m); N powmod(const N &x, N i, const N &m)}{$xy \bmod m$ by shift-and-add (avoids overflow of $xy$ up to about $m<2^{63}$), $x^i\bmod m$.} \end{lareference} Example: \code{uint64_t n=360; cout << factorization(n) << eulerphi(n);} prints \code{(2,3) (3,2) (5,1) 96}. %===================================================================== \section{Graph algorithms}\label{sec:utilities:graph} \laheader{graph.h, graph.cc} A small collection of graph algorithms. There is no graph class: the functions are templates in the adjacency matrix type \code{G}, which must provide \code{nrows()}, an element access \code{operator()(i,j)} convertible to \code{bool}, a copy constructor/assignment and \code{submatrix(const NRVec&)} returning a \code{G}. They are instantiated only for \code{G = NRSMat} (undirected graphs, chapter~\ref{ch:smat}). Vertices are numbered from 0; diagonal elements are ignored. \begin{lareference} \lafunc{NRVec neighbors(const G &adjacency, const int v); NRVec nonneighbors(const G &adjacency, const int v)}{Sorted lists of vertices adjacent / not adjacent to $v$ (excluding $v$).} \lafunc{NRVec findclique(const G &adjacency, const int v=0)}{A large clique found by the recursive Ramsey heuristic of Boppana and Halld\'orsson \cite{utilities:boppana1992} started at vertex $v$; returns the sorted vertex list (empty for an empty graph). Exponential worst case in recursion; intended for small graphs.} \lafunc{NRVec complement(const G &adjacency, const NRVec &vertexlist)}{Vertices not contained in \code{vertexlist}.} \lafunc{NRVec cliquecover(const G &adjacency, int *ncliques=NULL)}{Greedy clique cover by repeated \code{findclique} on the remaining subgraph; returns for each vertex the number of its clique (counted from 1) and optionally the number of cliques.} \end{lareference} \begin{lstlisting} NRSMat adj; cin >> adj; NRVec clique = findclique(adj); NRVec cover = cliquecover(adj); NRPerm p(cover.size()); cover.sort(0,p,true); //permutation grouping the cliques NRSMat adjperm = adj.permuted(p); //block structure \end{lstlisting}