ublas_matrixutil.cpp.html mathcode2html   
 Source file:   ublas_matrixutil.cpp
 Converted:   Sat Oct 3 2015 at 14:07:21
 This documentation file will not reflect any later changes in the source file.

$$\phantom{******** If you see this on the webpage then the browser could not locate *********}$$
$$\phantom{******** jsMath/easy/load.js or the variable root is set wrong in this file *********}$$
$$\newcommand{\vector}[1]{\left[\begin{array}{c} #1 \end{array}\right]}$$ $$\newenvironment{matrix}{\left[\begin{array}{cccccccccc}} {\end{array}\right]}$$ $$\newcommand{\A}{{\cal A}}$$ $$\newcommand{\W}{{\cal W}}$$

/* Copyright 2008-2014 Research Foundation State University of New York   */

/* This file is part of QUB Express.                                      */

/* QUB Express is free software; you can redistribute it and/or modify    */
/* it under the terms of the GNU General Public License as published by   */
/* the Free Software Foundation, either version 3 of the License, or      */
/* (at your option) any later version.                                    */

/* QUB Express is distributed in the hope that it will be useful,         */
/* but WITHOUT ANY WARRANTY; without even the implied warranty of         */
/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the          */
/* GNU General Public License for more details.                           */

/* You should have received a copy of the GNU General Public License,     */
/* named LICENSE.txt, in the QUB Express program directory.  If not, see  */
/* <http://www.gnu.org/licenses/>.                                        */

/*
See also: Up: Index
*/

#ifdef _WIN32
  #include <windows.h>
#else
  #include <stdlib.h>
  #define BOOL int
  #define TRUE 1
  #define FALSE 0
#endif

#include <string.h>
#include <iostream>
#include <algorithm>

#define HAVE_INLINE
#include <gsl/gsl_eigen.h>

#include "ublas_plus.h"
#include "ublas_matrixutil.h"

using std::cerr;
using std::cout;
using std::endl;
using std::min;

void verify_inverse(double **A, matrix<double> Ainv, bool retry_gaussj=false)
{
  int n = (int) Ainv.size1();
  pointy_matrix<double> Am(n, n);
  matrix_from_arrays(Am.m, A, n, n);
  matrix<double> product(n, n);
  noalias(product) = prod(Am.m, Ainv);
  double ssd = 0.0;
  for (int i=0; i<n; ++i) {
    for (int j=0; j<n; ++j) {
      
      double d = product(i, j);
      if ( (i == j) && (fabs(d) > .1) )
	d -= 1.0;
      ssd += d*d;
    }
  }
  if ( ssd/(n*n) > 1.0 ) {
    cerr << "Bad matrix inverse: ssd = " << ssd << endl;
    cerr << Am.m << endl;
    cerr << product << endl;
    
    if ( retry_gaussj ) {
      cerr << "trying gaussj..." << endl;
      pointy_matrix<double> Ag(n, n);
      matrix_from_arrays(Ag.m, A, n, n);
      gaussj0(Ag.pp, n, NULL, 0, cerr);
      verify_inverse(A, Ag.m, false);
    }
  }
} 

QUBFAST_API void invm(matrix<double>& Q_in, matrix<double>& Q_out)
{
  assert( Q_in.size1() == Q_in.size2() );
  int n = (int) Q_in.size1();
  fq::matrix<double> Q(n, n);//, Qi(n, n);
  matrix_to_arrays(Q_in, (double**)Q, n, n);
  //sv_invm((double**)Q, n, (double**)Qi);
  std::ostringstream xerr;
  gaussj0(Q, n, NULL, 0, xerr);
  std::string err = xerr.str();
  if ( ((int) Q_out.size1()) != n || ((int) Q_out.size2()) != n )
    Q_out.resize(n, n);
  matrix_from_arrays(Q_out, (double**)Q, n, n);
  if ( err.size() ) {
    cerr << "in invm: " << err << endl;
    cerr << Q_in << endl << endl;
    cerr << Q_out << endl << endl;
  }
  //verify_inverse((double**)Q, Q_out);
}
  
int print_count = 0;

bool invm_chk(matrix<double>& Q_in, matrix<double>& Q_out)
{
  assert( Q_in.size1() == Q_in.size2() );
  int n = (int) Q_in.size1();
  fq::matrix<double> Q(n, n);//, Qi(n, n);
  matrix_to_arrays(Q_in, (double**)Q, n, n);
  //sv_invm((double**)Q, n, (double**)Qi);
  std::ostringstream xerr;
  gaussj0(Q, n, NULL, 0, xerr);
  std::string err = xerr.str();
  if ( ((int) Q_out.size1()) != n || ((int) Q_out.size2()) != n )
    Q_out.resize(n, n);
  matrix_from_arrays(Q_out, (double**)Q, n, n);
  if ( err.size() ) {
    cerr << "in invm_chk: " << err << endl;
    cerr << Q_in << endl << endl;
    cerr << Q_out << endl << endl;
    return false;
  }
  return true;
  //verify_inverse((double**)Q, Q_out);
}
  
QUBFAST_API bool eigen_vals_vecs(matrix<double>& Q_in, double *rvalues, double *ivalues, matrix<double>& vectors_out, matrix<double>& vectors_inv, std::ostream& output)
{
  assert( Q_in.size1() == Q_in.size2() );
  int n = (int) Q_in.size1();
  if ( ((int) vectors_out.size1()) != n || ((int) vectors_out.size2()) != n )
    vectors_out.resize(n, n);
  if ( ((int) vectors_inv.size1()) != n || ((int) vectors_inv.size2()) != n )
    vectors_inv.resize(n, n);

  gsl_set_error_handler_off();

  gsl_matrix *Q = gsl_matrix_alloc(n,n);
  for (int i=0; i<n; ++i)
    for (int j=0; j<n; ++j)
      gsl_matrix_set(Q,i,j, Q_in(i, j));
  gsl_matrix_complex *V = gsl_matrix_complex_alloc(n,n);
  gsl_vector_complex *L = gsl_vector_complex_alloc(n);
  gsl_eigen_nonsymmv_workspace *ws = gsl_eigen_nonsymmv_alloc(n);
  gsl_eigen_nonsymmv(Q, L, V, ws);
  for (int i=0; i<n; ++i) {
    rvalues[i] = GSL_REAL(gsl_vector_complex_get(L, i));
    ivalues[i] = 0.0; // GSL_IMAG(gsl_vector_complex_get(L, i));
    for (int j=0; j<n; ++j)
      vectors_out(i, j) = GSL_REAL(gsl_matrix_complex_get(V,i,j));
  }
  gsl_matrix_free(Q);
  gsl_matrix_complex_free(V);
  gsl_vector_complex_free(L);
  gsl_eigen_nonsymmv_free(ws);

  invm(vectors_out, vectors_inv);
  return TRUE;

  /*
  fq::matrix<double> Q(n, n);
  matrix_to_arrays(Q_in, (double**)Q, n, n);
  fq::vector<double> r_1base(n+1), i_1base(n+1);
  fq::matrix<double> V(n, n);
  bool result = eigen((double**)Q, n, (double*)r_1base, (double*)i_1base, V, output);
  matrix_from_arrays(vectors_out, (double**)V, n, n);
  invm(vectors_out, vectors_inv);
  memcpy(rvalues, r_1base+1, n*sizeof(double));
  memcpy(ivalues, i_1base+1, n*sizeof(double));
  return result;
  */
}

QUBFAST_API void expm_eig(double *rvalues, matrix<double>& vectors, matrix<double>& vectors_inv, double t,
	      matrix<double>& tmp_nxn_diag, matrix<double>& tmp_nxn, matrix<double>& Q_out)
{
  int n = (int) vectors.size1();
  for (int i=0; i<n; ++i) {
    tmp_nxn_diag(i,i) = exp(rvalues[i] * t);
  }
  
  noalias(tmp_nxn) = prod(vectors, tmp_nxn_diag);
  noalias(Q_out) = prod(tmp_nxn, vectors_inv);
}  

QUBFAST_API void expm(matrix<double>& Q_in, double t, matrix<double>& Q_out)
{
  int n = (int) Q_in.size1();
  fq::vector<double> rvalues(n), ivalues(n);
  matrix<double> vectors;
  matrix<double> vectors_inv;
  matrix<double> tmp_nn(n, n);
  matrix<double> tmp_diag(n, n);
  for (int i=0; i<n; ++i)
    for (int j=0; j<n; ++j)
      tmp_diag(i,j) = 0.0;
  
  eigen_vals_vecs(Q_in, rvalues, ivalues, vectors, vectors_inv, cerr);
  expm_eig(rvalues, vectors, vectors_inv, t, tmp_diag, tmp_nn, Q_out);
}

#define TINY 1e-8
  
/* SVD like linpack:
#   * V is transposed
# but all matrices have full dimension
# Then we repair and check it:
#   * tiny numbers are replaced with 0
#   * U and V need orthonormal columns
*/

bool sv_decomp_inner(double** uu, int Nr, int Nc, matrix<double>& U, matrix<double>& W, matrix<double>& Vi);

QUBFAST_API bool sv_decomp(matrix<double>& A, matrix<double>& U, matrix<double>& W, matrix<double>& Vi)
{
  int Nr = (int) A.size1();
  int Nc = (int) A.size2();
  fq::matrix<double> uu(Nr+1, Nc+1);
  matrix_to_arrays(A, (double**) uu, Nr, Nc);
  return sv_decomp_inner(uu, Nr, Nc, U, W, Vi);
}

QUBFAST_API bool sv_decomp(double** A, int Nr, int Nc, matrix<double>& U, matrix<double>& W, matrix<double>& Vi)
{
  fq::matrix<double> uu(Nr+1, Nc+1);
  for (int i=0; i<Nr; ++i)
    for (int j=0; j<Nc; ++j)
      uu[i][j] = A[i][j];
  return sv_decomp_inner(uu, Nr, Nc, U, W, Vi);
}

bool sv_decomp_inner(double** uu, int Nr, int Nc, matrix<double>& U, matrix<double>& W, matrix<double>& Vi)
{  
  // automatic storage
  fq::vector<double> _ww(Nc+1);
  fq::matrix<double> _vv(Nc+1, Nc+1);
  // as pointers
  double *ww = _ww, **vv = _vv;
  // shifted to 1-based
  for (int i=0; i<Nr; ++i)
    uu[i] -= 1;
  for (int i=0; i<Nc; ++i)
    vv[i] -= 1;
  uu -= 1; ww -= 1; vv -= 1;
  // SVD
  svdecomp(uu, Nr, Nc, ww, vv, cerr);
  // shifted to 0-based
  uu += 1; ww += 1; vv += 1;
  for (int i=0; i<Nr; ++i)
    uu[i] += 1;
  for (int i=0; i<Nc; ++i)
    vv[i] += 1;
  // outputs
  if ( ((int) U.size1()) != Nr || ((int) U.size2()) != Nc )
    U.resize(Nr, Nc);
  for (int i=0; i<Nr; ++i)
    for (int j=0; j<Nc; ++j)
      U(i, j) = (fabs(uu[i][j]) > TINY) ? uu[i][j] : 0.0;
  if ( ((int) W.size1()) != Nc || ((int) W.size2()) != Nc )
    W.resize(Nc, Nc);
  for (int i=0; i<Nc; ++i)
    for (int j=0; j<Nc; ++j)
      W(i, j) = ((i == j) && (fabs(ww[i]) > TINY)) ? ww[i] : 0.0;
  if ( ((int) Vi.size1()) != Nc || ((int) Vi.size2()) != Nc )
    Vi.resize(Nc, Nc);
  for (int i=0; i<Nc; ++i)
    for (int j=0; j<Nc; ++j)
      Vi(i, j) = (fabs(vv[i][j]) > TINY) ? vv[i][j] : 0.0;

  // checks a la MIL
  double s;
  for (int i=0; i<Nr; i++) {
    for (int j=Nr; j<Nc; j++) {
      if(fabs(uu[i][j])>TINY) {
        return false;
      }
    }
  }
  int Nlo = min(Nr, Nc);
  for (int i=0; i<Nlo; i++) { // make sure that u's columns are orthogonal unit vectors
    for (int j=0; j<Nlo; j++) { // changed from nx to mc Chris 6/2/2
      s=0.0;
      for (int k=0; k<Nr; k++) 
        s=s+uu[k][i]*uu[k][j];
      if( (i!=j&&fabs(s)>TINY) || (i==j&&fabs(s-1.0)>TINY) ) {
        return false;
      }
    }
  }
  for (int i=0; i<Nc; i++) { // make sure that v's columns are orthogonal unit vectors
    for (int j=0; j<Nc; j++) {
      s=0.0;
      for (int k=0; k<Nc; k++) 
        s=s+vv[k][i]*vv[k][j];
      if( (i!=j && fabs(s)>TINY) || (i==j && fabs(s-1.0)>TINY) ){
        return false;
      }
    }
  }
  for (int i=0; i<Nc; i++) {
    for (int j=0; j<Nc; j++) {
      s=0.0;
      for (int k=0; k<Nc; k++) 
        s=s+vv[i][k]*vv[j][k];
      if( (i!=j&&fabs(s)>TINY) || (i==j&&fabs(s-1.0)>TINY) ){
        return false;
      }
    }
  }  
  
  return true;
}

QUBFAST_API bool sv_invm(double** A, int n, double** Ai)
{
	matrix<double> U(n, n), W(n, n), Vi(n, n);
	bool result = sv_decomp(A, n, n, U, W, Vi);
	for (int i=0; i<n; ++i) {
		if ( W(i,i) )
			W(i,i) = 1.0 / W(i,i);
	}
	matrix<double> out(n, n);
	noalias(out) = prod(Vi, matrix<double>(prod(W, trans(U))));
	matrix_to_arrays(out, Ai, n, n);
	return result;
}