Main Page | Namespace List | Class Hierarchy | Class List | Directories | File List | Namespace Members | Class Members | File Members | Examples

matrix.h

Go to the documentation of this file.
00001 /* A Quantum Information Matrix Toolkit
00002  * 
00003  * The last thing the world needs is another C++ Matrix library. However ...
00004  *
00005  * This code is intended to facilitate coding C++ numerics related to Quantum
00006  * Information. Naturally you'd probably be better off using Matlab or   
00007  * something but I like C++ and enjoy coding in it. This library provides:
00008  *  - A templated Matrix class with all the usual arithmetic operators and
00009  * some additional functions commonly used in Quantum Mechanics (such as the
00010  * partial trace and tensor product etc).
00011  *  - A convenient interface to certain Lapack linear algebra routines for
00012  * inverses, eigen and singular value decompositions.
00013  *  - Routines for the calculation of various entanglement measures. 
00014  *
00015  * Source code: http://www.physics.uq.edu.au/people/dawson/matrix/matrix-0.2.tar.gz
00016  *
00017  * Makefiles are provided for Linux (tested with g++ 2.95 and 3.2.1) and Windows
00018  * (Visual C++ 7) which compile shared objects / dlls. You'll also need the 
00019  * Lapack and Blas libraries which are provided by most major linux
00020  * distributions. For Windows I recommend the CLaw32 stuff from
00021  * http://www.netlib.org/clapack/
00022  *
00023  * Alternatively for windows you can download precompiled dlls
00024  *
00025  * Win32 libraries: http://www.physics.uq.edu.au/people/dawson/matrix/matrixw32-0.2.zip
00026  * 
00027  * This document is somewhat incomplete.  
00028  *
00029  * If you make any improvements, fix any bugs, or anything please let me know.
00030  *
00031  * dawson@physics.uq.edu.au */
00032 
00033 #ifndef _MATRIX_
00034 #define _MATRIX_
00035 #include <complex>
00036 #include <iostream>
00037 #include <fstream>
00038 #include <cstdlib>
00039 
00040 #define PI 3.1415926535897932384626
00041 #define SQRT2 1.41421356237309504880169
00042 #define EABS(a) (abs(a)<1e-15?0.0:abs(a))
00043 #define EVAL(a) (abs(a)<1e-14?0.0:a)
00044 //#define SWAP(a,b) {temp=a;a=b;b=temp;}
00045 
00046 template <typename Z> static Z gconj(const Z &other) { return other; }
00047 template <> static std::complex<double>
00048 gconj<std::complex<double> >(const std::complex<double> & other) {
00049      return std::conj(other);
00050 }
00051 
00056 template <typename Z> class Matrix {
00057 public:
00058 
00059     Z *A;               
00060     unsigned int M;     
00061     unsigned int N;     
00062 
00063     Z* scratch;         
00064 
00065     Matrix(void) : M(0), N(0), A(0), scratch(0) {}
00066 
00068 
00069     Matrix(unsigned int Rows, unsigned int Cols);
00070 
00072 
00075     Matrix(Z *elements ,unsigned int Rows, unsigned int Cols);
00076 
00077     Matrix(const Matrix &A);                
00078     Matrix& operator =(const Matrix &C);    
00079 
00080     ~Matrix();
00081 
00083     inline Z&  operator() (unsigned int i, unsigned int j);
00084     const Z&  operator() (unsigned i, unsigned j) const { return A[j*M+i]; }
00085 
00087 
00090     Matrix& operator+=(const Matrix& M);
00091 
00093     Matrix& operator-=(const Matrix& M);
00094 
00096     /* Again this is the more efficient way to do multiplication. This 
00097      * should take a const reference. Unfortunately this clashes with the
00098      * overloads below and I have no idea how to reconcidle them. */
00099     Matrix& operator*=(Matrix M);
00100 
00102     /* This is templated so you can conveniently multiply a std::complex matrix
00103      * by an integer and so on. (Naturally you wouldn't want to try it the
00104      * other way round) */
00105 
00106     #ifdef _MSC_VER
00107     template<typename S>
00108     Matrix& operator*=(const S &c) {
00109         for (unsigned i=0;i<M*N;i++)
00110             A[i] *= c;
00111         return *this;
00112     }
00113     #else
00114     template <typename S> Matrix& operator*=(const S& s);
00115     #endif
00116 
00118     #ifdef _MSC_VER
00119     friend std::ostream& operator<<(std::ostream&, const Matrix<Z>&);
00120     #else
00121     friend std::ostream& operator<<<>(std::ostream&, const Matrix&);
00122     #endif
00123 
00125     void dump(const char* filename);    
00127     void read(const char* filename);
00128 };
00129 
00141 template <typename Z>
00142 Matrix<Z> operator+(const Matrix<Z>&, const Matrix<Z>&);
00143 
00144 template <typename Z>
00145 Matrix<Z> operator-(const Matrix<Z> &, const Matrix<Z> &);
00146 
00147 
00148 template <typename Z>
00149 Matrix<Z> operator*(const Z &, const Matrix<Z> &);
00150 
00151 #ifndef _MSC_VER
00152 template <typename Z, typename S>
00153 Matrix<Z> operator*(const S&, const Matrix<Z> &);
00154 #else
00155 template <typename Z, typename S>
00156 Matrix<Z> operator*(const S &a, const Matrix<Z> &B) {
00157     Matrix<Z> R = B;
00158     return R*=a;
00159 }
00160 #endif
00161 
00162 template <typename Z>
00163 Matrix<Z> operator*(const Matrix<Z> &, const Matrix<Z> &);
00165 
00179 
00181 
00183 template <typename Z>
00184 Matrix<Z> Adjoint(const Matrix<Z>&);
00185 
00187 template <typename Z>
00188 Z Trace(Matrix<Z>&);
00189 
00198 template <typename Z>
00199 Matrix<Z> PTrace(Matrix<Z>& R, unsigned dA, unsigned dB);
00200 
00202 template <typename Z>
00203 Matrix<Z> TensorProduct(Matrix<Z>& A, Matrix<Z>& B);
00204 
00209 template <typename Z>
00210 Z Determinant(Matrix<Z>& M); 
00211 
00215 template <typename Z>
00216 void SubMatrix(Matrix<Z>& M, Matrix<Z>& S, unsigned i, unsigned j);
00218 
00221 
00222 //template <typename Z>
00223 //std::ostream& operator<<(std::ostream& output, const Matrix<Z>& A);
00225 
00226 /* ----------------------------------------------------------------------- *
00227  * End of Prototypes and DOXYGEN comments
00228  * Start of Implementation
00229  * ----------------------------------------------------------------------- */
00230 
00231 template<typename Z>
00232 Matrix<Z>::Matrix(unsigned int m, unsigned int n) : M(m), N(n) {
00233     A = new Z[m*n];
00234     scratch = new Z[n];
00235 }
00236 
00237 template<typename Z>
00238 Matrix<Z>::Matrix(Z *a,unsigned int m, unsigned int n) : M(m), N(n) {
00239     A = new Z[m*n];
00240     scratch = new Z[n];
00241     for (unsigned i=0;i<n;i++) 
00242         for (unsigned j=0;j<m;j++)
00243             A[i*m+j] = a[n*j+i];
00244 }
00245 
00246 template<typename Z>
00247 Matrix<Z>::Matrix(const Matrix<Z> &t) : M(t.M), N(t.N) {
00248     A = new Z[t.M*t.N];
00249     scratch = new Z[N];
00250     for (unsigned i=0;i<t.M*t.N;i++) A[i] = t.A[i];
00251 }
00252 
00253 template<typename Z>
00254 Matrix<Z>& Matrix<Z>::operator =(const Matrix<Z> &t) {
00255     if (this != &t) {
00256         if (N != t.N || M != t.M) {
00257             delete[] A;
00258             delete[] scratch;
00259             A = new Z[t.M*t.N];
00260             scratch = new Z[t.N];
00261             M = t.M; N = t.N;
00262         }
00263         for (unsigned i=0;i<M*N;i++) A[i] = t.A[i];
00264     }
00265     return *this;
00266 }
00267 
00268 template<typename Z>
00269 Matrix<Z>::~Matrix() {
00270     delete[] A;
00271     delete[] scratch;
00272 } 
00273 
00274 template<typename Z>
00275 inline Z& Matrix<Z>::operator() (unsigned int i, unsigned int j) {
00276     return A[j*M+i];
00277 }
00278 
00279 template<typename Z>
00280 Matrix<Z>& Matrix<Z>::operator+=(const Matrix<Z> &B) {
00281     for (unsigned i=0;i<M*N;i++)
00282         A[i] += B.A[i];
00283     return *this;
00284 }
00285 
00286 template<typename Z>
00287 Matrix<Z>& Matrix<Z>::operator-=(const Matrix<Z> &B) {
00288     for (unsigned i=0;i<M*N;i++)
00289         A[i] -= B.A[i];
00290     return *this;
00291 }
00292 
00293 template<typename Z>
00294 Matrix<Z>& Matrix<Z>::operator*=(Matrix<Z> B) {
00295 
00296     // First check this makes sense
00297     if (N != B.M)
00298         std::cerr << "Incompatible matrices passed to Matrix *=\n";
00299 
00300     // And ensure we're not post-multiplying by ourself
00301     if (this == &B)
00302     {
00303         Matrix<Z> TMP = B;
00304         return operator*=(TMP);
00305     }
00306 
00307     // If B has more columns than A we'll need to resize
00308     if (B.N > N)
00309     {
00310         Z *tmp = new Z[M*B.N];
00311         memcpy(tmp,A,M*N*sizeof(Z));
00312         delete[] A;
00313         A = tmp;
00314 
00315         delete[] scratch;
00316         scratch = new Z[B.N];
00317     }
00318 
00319     for (unsigned i=0;i<M;i++)
00320     {
00321         // copy this row of *this  into scratch
00322         for (unsigned j=0;j<N;j++)
00323             scratch[j] = A[j*M+i];
00324 
00325         // now for each jth column of B
00326         for (unsigned j=0;j<B.N;j++) 
00327         {
00328             // Set A(i,j) equal to A(i,*) . B(*,j)
00329             A[i+M*j] = 0;
00330             for (unsigned k=0;k<N;k++) 
00331                 A[i+M*j] += (scratch[k] * B.A[k+j*B.M]);
00332         }
00333     }
00334 
00335     N = B.N;
00336     return *this;
00337 }
00338 
00339 #ifndef _MSC_VER
00340 template<typename Z> template<typename S>
00341 Matrix<Z>& Matrix<Z>::operator*=(const S &c)
00342 {
00343     for (unsigned i=0;i<M*N;i++)
00344         A[i] *= c;
00345     return *this;
00346 }
00347 #endif
00348 
00349 template<typename Z> void Matrix<Z>::dump(const char *fn) {
00350     std::ofstream out_file;
00351 
00352     out_file.open(fn, std::ios::out | std::ios::binary);
00353 
00354     if (out_file.bad()) {
00355         std::cerr << "Unable to open matrix file for writing" << fn << std::endl;
00356         exit(0);
00357     }
00358 
00359     out_file.write(static_cast<unsigned int *>(&M),sizeof(unsigned int));
00360     out_file.write(static_cast<unsigned int *>(&N),sizeof(unsigned int));
00361 
00362     out_file.write(A,N*M*sizeof(Z));
00363 
00364     if (out_file.bad()) {
00365         std::cerr << "Unable to write matrix file " << fn << std::endl;
00366         exit(0);
00367     }
00368 
00369     out_file.close();
00370 }
00371 
00372 
00373 template<typename Z> void Matrix<Z>::read(const char *fn) {
00374     unsigned int m,n;
00375     std::ifstream in_file;
00376 
00377     in_file.open(fn, std::ios::in | std::ios::binary);
00378 
00379     if (in_file.bad()) {
00380         std::cerr << "Unable to open matrix file " << fn  
00381             << " for reading\n";
00382         exit(0);
00383     }
00384 
00385     in_file.read(static_cast<unsigned int *>(&m),sizeof(unsigned int));
00386     in_file.read(static_cast<unsigned int *>(&n),sizeof(unsigned int));
00387 
00388     // resize if necessary  
00389     if (n<N || m<M) {
00390         delete[] A;
00391         A = new Z[n*m];
00392     }
00393     N = n; M = m;
00394 
00395     in_file.read(A,N*M*sizeof(Z));
00396 
00397     if (in_file.bad()) {
00398         std::cerr << "Unable to read matrix file " << fn << std::endl;
00399         exit(0);
00400     }
00401 
00402     in_file.close();
00403 }
00404 
00405 /* ----------------------------------------------------------------------- *
00406  * End of Member Functions
00407  * ----------------------------------------------------------------------- */
00408 
00409 template <typename Z>
00410 Matrix<Z> operator*(const Matrix<Z> &A, const Matrix<Z> &B)
00411 {
00412     Matrix<Z> R = A;
00413     return R*=B;
00414 }
00415 
00416 template <typename Z>
00417 Matrix<Z> operator+(const Matrix<Z> &A, const Matrix<Z> &B) {
00418     Matrix<Z> R = A;
00419     return R+=B;
00420 }
00421 
00422 template <typename Z>
00423 Matrix<Z> operator-(const Matrix<Z> &A, const Matrix<Z> &B) {
00424     Matrix<Z> R = A;
00425     return R-=B;
00426 }
00427 
00428 template <typename Z>
00429 Matrix<Z> operator*(const Z &a, const Matrix<Z> &B) {
00430     Matrix<Z> R = B;
00431     //std::cout << "*Z &a\n";
00432     return R*=a;
00433 }
00434 
00435 #ifndef _MSC_VER
00436 template <typename Z, typename S>
00437 Matrix<Z> operator*(const S &a, const Matrix<Z> &B) {
00438     Matrix<Z> R = B;
00439     return R*=a;
00440 }
00441 #endif
00442 
00443 template <typename Z>
00444 Matrix<Z> Adjoint(const Matrix<Z>& U) {
00445     Matrix<Z> ADJ(U.N,U.M);
00446     for (unsigned i=0;i<U.M;i++) 
00447         for(unsigned j=0;j<U.N;j++) 
00448         ADJ(j,i) = gconj<Z>(U(i,j));
00449     return ADJ;
00450 }
00451 
00452 template<typename Z>
00453 Z Trace(Matrix<Z>& A) {
00454     Z result = 0;
00455     for (unsigned i=0;i<A.M;i++)
00456         result += A(i,i);
00457 
00458     return result;
00459 }
00460 
00461 template <typename Z>
00462 std::ostream& operator<<(std::ostream& output, const Matrix<Z>& A) {
00463     output << "[\n";
00464     for (unsigned i=0;i<A.M;i++) {
00465         for (unsigned j=0;j<A.N;j++) 
00466             //output << "\t" << A(i,j) << "\t";
00467             output << "\t" << A.A[j*A.M+i] << "\t";
00468         output << "\n";
00469     }
00470     output << "]";
00471 
00472     return output; 
00473 }
00474 
00475 template <typename Z>
00476 void SubMatrix(Matrix<Z> &P, Matrix<Z> &C, unsigned r, unsigned c) {
00477 
00478     for (unsigned i=0;i<r;i++) {
00479         for (unsigned j=0;j<c;j++)
00480             C(i,j) = P(i,j);
00481         for (unsigned j=c+1;j<P.N;j++)
00482             C(i,j-1) = P(i,j);
00483     }
00484 
00485     for (unsigned i=r+1;i<P.M;i++) {
00486         for (unsigned j=0;j<c;j++)
00487             C(i-1,j) = P(i,j);
00488         for (unsigned j=c+1;j<P.N;j++)
00489             C(i-1,j-1) = P(i,j);
00490     }
00491 }
00492 
00493 template<typename Z>
00494 Matrix<Z> TensorProduct(Matrix<Z>& A,Matrix<Z>& B) {
00495 
00496     if (A.M == 1 && A.N == 1) {
00497         return B;
00498     }
00499 
00500     if (B.M == 1 && B.N == 1)
00501         return A;
00502 
00503     unsigned rows = A.M*B.M;
00504     unsigned cols = A.N*B.N;
00505 
00506     Matrix<Z> M(rows,cols);
00507 
00508     for (unsigned i=0;i<rows;i++) 
00509         for (unsigned j=0;j<cols;j++) 
00510             M(i,j) = A(i/B.M,j/B.N) * B(i%B.M,j%B.N);
00511 
00512     return M;
00513 }
00514 
00515 
00516 template<typename Z>
00517 Matrix<Z> PTrace(Matrix<Z>& M, unsigned dA, unsigned dB) {
00518     // check this is actually possible
00519     //assert((M.M % (dA*dB)) == 0);
00520 
00521     // figure out how big the returned matrix will be
00522     unsigned D = M.M / dB;
00523     unsigned dC;
00524 
00525     // if there is an offset of 0, just consider this a
00526     // 1d system for simplicity
00527     if (dA == 0)
00528         dA = 1;
00529 
00530     // figure out the size of subsystem C
00531     dC = D / dA;
00532 
00533     Matrix<Z> R(D,D), IA(dA,dA), IC(dC,dC), B(1,dB);
00534     // Tensor product I_A x <e| I_C
00535     Matrix<Z> T(D,M.M);
00536 
00537     B(0,0) = 1;
00538     for (unsigned i=1;i<dB;i++)
00539         B(0,i) = 0;
00540 
00541     for (unsigned i=0;i<dA;i++) {
00542         IA(i,i) = 1;
00543         for (unsigned j=0;j<i;j++)
00544             IA(i,j) = IA(j,i) = 0;
00545     }
00546 
00547     for (unsigned i=0;i<dC;i++) {
00548         IC(i,i) = 1;
00549         for (unsigned j=0;j<i;j++)
00550             IC(i,j) = IC(j,i) = 0;
00551     }
00552         
00553     // now do the partial trace
00554     Matrix<Z> TMP = TensorProduct(B,IC);
00555     T = TensorProduct(IA,TMP);
00556     R = T*M*Adjoint(T);
00557 
00558     for (unsigned i=1;i<dB;i++) {
00559         B(0,i-1) = 0; B(0,i) = 1;
00560         TMP = TensorProduct(B,IC);
00561         T = TensorProduct(IA,TMP);
00562         R += (T*M*Adjoint(T));
00563     }
00564 
00565     return R;
00566 }
00567 
00568 template <typename Z>
00569 Z Determinant(Matrix<Z>& M) {
00570     Z d; int s;
00571     if (M.M == 2)
00572         return M(0,0)*M(1,1)-M(1,0)*M(0,1);
00573 
00574     d = (Z)0; s = 1;
00575 
00576     Matrix<Z> S(M.M-1,M.N-1);
00577 
00578     for (unsigned i=0;i<M.M;i++) {
00579         SubMatrix(M,S,0,i);
00580         d += static_cast<const Z&>(s)*M(0,i) * Determinant(S);
00581         s *= -1;
00582         
00583     }
00584     return d;
00585 }
00586 
00587 #endif

Generated on Sun Jul 10 21:57:13 2005 by  doxygen 1.4.1