view include/linalg.hpp @ 16:29a7b95c2805

Essentially all Doxygen docstrings complete.
author Jordi Gutiérrez Hermoso <jordigh@gmail.com>
date Sun, 10 Aug 2008 22:13:09 -0500
parents 5144dd3c5468
children 72412107029b
line wrap: on
line source

/*!\file linalg.hpp
 * \brief Wrapper linear algebra classes for the GSL.
 *
 * This header file puts some C++ wrappers around the GSL vector and
 * matrix structures plus providing a C++ interface to some pertinent
 * BLAS and general linear algebra routines.
 * 
 *  \file linalg.cpp
 *  \brief Implementations and instantiations of the functions and
 *  classes declared in linalg.hpp
 */

#ifndef __LINALG_HPP__
#define __LINALG_HPP__

#include <gsl/gsl_matrix.h>
#include <gsl/gsl_vector.h>
#include <gsl/gsl_permutation.h>
#include "error.hpp"

/// Linear algebra namespace
namespace linalg{
  using namespace error_handling;

  class slice; 
  class vector;
  class vector_view;

  /// A wrapper class for GNU Scientific Library matrices
  class matrix{
  private:
    class LUmatrix;

  public:
    //Constructors

    /// Allocate 1x1 matrix zero matrix.
    matrix(); 
    /*! \brief Allocate m by n matrix, filled with some value.
      \param m - Number of rows.
      \param n - Number of columns.
      \param fillvalue - Value to fill matrix with, default is 0.
     */
    matrix(const size_t m, const size_t n, const double fillvalue = 0) ;
    /// Assign a matrix from a GSL matrix pointer.
    matrix(gsl_matrix *M);

    /// Copy constructor
    matrix(const matrix& M);
    /// Assignment.
    matrix operator=(const matrix& M);
    
    //Destructors
    
    /// Clears the memory allocated by the GSL
    ~matrix();

    /// Number of decimal digits to output.
    size_t precision() const;
    /// Set the number of decimal digits to output.
    static void set_precision(size_t p);

    /// Number of rows.
    size_t rows() const;
    /// Number of columns.
    size_t cols() const;

    /*! \brief Fortran-style parenthetical indexing (hence
     *         Octave-style too)
     *
     *  Indices start from 1.
     *  @param i - Row number.
     *  @param j - Column number.
     *  @return A reference to the matrix element.
     */
    double& operator()(const size_t i, const size_t j);
    
    /// Constant version of previous function.
    const double& operator()(const size_t i, const size_t j) const;

    /*! \brief Indexing for vectorisation.
     *
     *  The GSL provides limited vectorisation routines which can be
     *  useful for operating on blocks of matrices all at once.
     *  @param i - Row to be sliced.
     *  @param b - Slice range.
     *  @return A vector_view of the sliced row.
     */
    vector_view operator()(const size_t  i, const slice &b);
    /// Constant version of previous function.
    const vector_view operator()(const size_t  i, const slice &b) const;

    /*! \brief Indexing for vectorisation.
     *
     *  The GSL provides limited vectorisation routines which can be
     *  useful for operating on blocks of matrices all at once.
     *  @param a - Slice range
     *  @param j - column to be sliced.
     *  @return A vector_view of the sliced column.
     */
    vector_view operator()(const slice &a, const size_t  j);
    /// Constant version of previous function.
    const vector_view operator()(const slice &a, const size_t  j) const;

    //Arithmetic operations
    ///Scale the matrix.
    matrix operator*(const double  a) const;
    /// Matrix addition
    matrix operator+(const matrix& N) const;
    /// Matrix multiplication
    matrix operator*(const matrix& N) const;
    /// Matrix subtraction
    matrix operator-(const matrix& N) const;
    /// Matrix vector product (gaxpy operation).
    vector operator*(const vector& v) const; //Mv, where v is treated
					     //as a column vector. 

    //More complex operations.

    /*! \brief Inverse.
     *
     * This function returns the inverse matrix, computed via an LU
     * factorisation. If the matrix is ill-conditioned or the inverse
     * does not exist, it will happily return a matrix full of NaN.
     * \returns The inverse matrix.
     */
    matrix inv()   const;    
    /*! \brief Tranpose.
     *
     * Returns a copy of the original matrix, transposed. The original
     * matrix is not modified.
     * \returns Transposed copy of matrix.
     */
    matrix T()     const;    
    /*! \brief Trace.
     *
     * The trace of a square matrix.
     * \returns The trace.
     */
    double tr()    const;    
    /*! \brief Determinant.
     *
     * The determinant computed via an LU factorisation
     * \returns The determinant.
     */
    double det()   const;    

    /*! \brief Solves Mv = w for v with LU factorisation. 
     *
     * Given another vector, will return the solution vector v to the
     * equation Mv = w.
     * \param w - Another vector, must have the same size as number of
     * rows in the matrix.
     * \returns The solution v to the equation Mv = w.
    */
    vector inv(const vector& w) const; 

    /*! \brief L2 condition number, using SVD.
     *
     * This function computes the L2 condition number of a matrix, the
     * largest singular value divided by the smallest. It directly
     * uses the SVD decomposition (as provided by the GSL), and it may
     * take a long time for large matrices.
     * \returns The L2 condition number.
     */
    double cond() const; 

    friend class vector_view;
  private:
    /*! \brief LU decomposition, pivots in U.
     *
     * This function will compute the LU factorisation of a matrix,
     * where the pivots are in the upper triangular matrix U and L has
     * ones along the diagonal (not stored). For efficiency, L and U
     * are both internally stored within a single matrix.
     * \returns A pointer to the private class LUmatrix.
     */ 
    LUmatrix* LU() const;    

    gsl_matrix * A;

    ///Precision
    static size_t precsn;
    /// Machine epsilon
    static const double eps;

    // Internal members, all can change, none of them affect the
    // actual matrix per se.
    mutable LUmatrix* LUptr;
    mutable bool LUfactored;

    mutable double condition_number;  //L2 condition number, obtained by svd.
    mutable gsl_vector* SVD;          //Matrix's singular values.
    mutable bool SVDfactored;

    void SVDfactor() const;

    /// A private matrix member class for matrices factored in LU form.
    class LUmatrix{ 
    public:
      gsl_matrix* matrix_ptr();
      gsl_permutation* perm_ptr();
      int sgn();
      int* sgn_ptr();

      LUmatrix(gsl_matrix* M);
      LUmatrix(const LUmatrix& LU);
      LUmatrix operator=(const LUmatrix& LU);
      ~LUmatrix();
    private: 
      // Private interface to the GSL.
      gsl_permutation* p;
      gsl_matrix* A;
      int signum;
      LUmatrix();
    };//End LUmatrix
  }; //End matrix

  /// A wrapper class for GSL vectors
  class vector{
  public: 
    //Constructors

    ///Allocate zero vector of size one.
    vector(); 
    /*! \brief Allocate GSL vector of size n, filled with fillvalue
     * \param n - Size of new vector
     * \param fillvalue - Value for filling vector, default is 0.
     */
    vector(const size_t n, const double fillvalue = 0);
    /// Associate a  vector to a GSL vector pointer.
    vector(gsl_vector *y);
    /// Associate a  vector to a constant GSL vector pointer.
    vector(const gsl_vector *y);

    /// Copy contstructor.
    vector(const vector& y);
    /// Assignment.
    vector& operator=(const vector &y);

    //Destructor
    /// Clear memory assigned to vector by GSL.
    ~vector();

    //Number of decimal digits to output.
    /// Number of decimal digits to output
    size_t precision() const;
    /// Set the number of decimal digits to output
    static void set_precision(size_t p);

    ///Number of elements
    size_t size() const;

    /*! \brief Fortran-style parenthetical indexing (hence
     *   Octave-style too). Indices start at 1.
     * \param i - Index number.
     * \return A reference to the vector element.
     */
    double& operator()(const size_t i);

    /// Constant version of previous function.
    const double& operator()(const size_t i) const;
  
    /*! \brief Indexing for vectorisation.
     *  The GSL provides limited vectorisation routines which can be
     *  useful for operating on blocks of vectors all at once.
     *  @param v - Slice range.
     *  @return A vector_view of the slice.
     */
    vector_view operator()(const slice &v);
    ///Constant version of previous function.
    const vector_view operator()(const slice &v) const;

    //Arithmetic operations:
    ///Scale the vector.
    vector operator*(const double a)  const;
    ///Scale the vector.
    vector operator/(const double a)  const;
    ///Vector addition.
    vector operator+(const vector& w) const;
    ///Vector subtration.
    vector operator-(const vector& w) const;
    ///Element-by-element multiplication.
    vector operator*(const vector& w) const; 
    ///Element-by-element division.
    vector operator/(const vector& w) const; 
    ///Computes vM where v is treated as a row vector.
    vector operator*(const matrix& M) const; 

    //Comparison
    
    /// Compares vectors elementwise, using machine epsilon.
    bool operator==(const vector& w) const;
    /*! \brief Lexicographical order, used for putting into STL sets or
     * maps.
     *
     * Lexicographical ordering of vectors. Vectors of smaller
     * dimension are smaller.
     */
    bool operator<(const vector& w) const; 

    //More complex operations
    ///Euclidean norm.
    double norm() const; 
    
    ///Dot product
    double operator%(const vector& w) const;

    friend class vector_view;
    friend class matrix;
  private:
    /// Pointer to associated GSL vector.
    gsl_vector * x;
    /// Output precision.
    static size_t precsn;
    /// Machine epsilon
    static const double eps;
  };


  /*! \brief  A vector that doesn't own its data; rather, points to data owned
   * by another vector. 
   */
  class vector_view : public vector{
    friend class vector;
    friend class matrix;
  public:
    ~vector_view();
    /// Assignment to a vector; will share same data in memory as that
    /// other vector.
    vector_view& operator=(const vector& w);
    /// Assignment, useful for modifying chunks of vectors all at once.
    vector_view& operator=(const vector_view& w);
  private:
    /// vector_views are invisible to the user and can only
    /// be created using slices and matrix or vector indexing.
    vector_view();
    /// See above.
    vector_view(gsl_vector* y, const slice& s);
    /// See above.
    vector_view(gsl_matrix* A, const slice& a, const size_t j);
    /// See above.
    vector_view(gsl_matrix* A, const size_t i, const slice& b);
  };

  
  /*! \brief Vector slices corresponding to GNU Octave ranges.
   *
   * Vector slices are useful for certain vectorisation routines and
   * for referring to rows, columns, or equally spaced chunks of a
   * vector or a matrix. Set a begininning and an end for the
   * stride, and a step size, and this will be the elements that can
   * be indexed by this slice in a vector or matrix. E.g. if the
   * beginning is 2, the end 8, and the stride is 2, then this
   * refers to the slice [2, 4, 6, 8]. The default stride is 1.
   *
   * Unlike GNU Octave slices, there is no shorthand for referring
   * to the entirety of a column or row of a matrix. Must instead
   * create a slice for that purpose as s(1,n) where n is the number
   * of rows or columns respectively.
   */
  class slice{
  public:
    /*! \brief Create a vector slice.
     * 
     * \param a - Beginning of the slice.
     * \param b - End of the slice.
     * \param k - Stride (default is 1).
     */
    slice(size_t a, size_t b, size_t k=1); 

    /*! For setting the slice parameters anew.
     *
     * Indices start from 1.
     * \param a - Beginning of the slice.
     * \param b - End of the slice.
     * \param k - Stride (default is 1).
     */
    slice operator()(size_t a, size_t b, size_t k=1);
    slice set(size_t a, size_t b, size_t k=1) ;

    size_t begin()const {return beg;};
    size_t end() const{return fin;};
    size_t stride() const{return str;};

  private:
    /// Empty and private default constructor.
    slice(){};
    size_t beg,fin;
    size_t str;    
  };
  
  /// Useful alias, vectors are also points in space.
  typedef vector point; //Useful alias.
}

namespace linalg{ //Non-member functions.
  
  /// I/O
  //@{
  ///Stream insertion operator
  std::ostream& operator<<(std::ostream& os, const vector &v);
  ///Stream extraction operator
  vector operator>>(std::istream& is, vector& v);
  ///Stream insertion operator
  std::ostream& operator<<(std::ostream& os, const matrix &M);
  ///Stream extraction operator
  matrix operator>>(std::istream& is, matrix& v);
  //@}

  ///Some arithmetic functions for comfortable syntax.
  //@{

  /// Scale a vector.
  vector operator*(double a, const vector& v);
  /// Euclidean norm of a vector.
  double norm(const vector& v);

  /// Scale a matrix.
  matrix operator*(double a, const matrix& M);
  /// Matrix inverse, computed with LU factorisation.
  matrix inv(const matrix& A);
  /// Return copy of transposed matrix.
  matrix T(const matrix& A);          
  /// Trace.
  double tr(const matrix& A);
  /// Determinant.
  double det(matrix& A);        
  /// L2 condition number, computed with SVD.
  double cond(matrix& A);
  //@}
}

//Inlined functions
namespace linalg{
  inline double& matrix::operator()(const size_t i, const size_t j){
    try{
      if(LUfactored)
	delete LUptr;
      if(SVDfactored)
	gsl_vector_free(SVD);
  
      LUfactored = false;
      SVDfactored = false;
      return(*gsl_matrix_ptr(A,i-1,j-1));
    }
    catch(indexOutOfRange& exc){
      exc.i = i;
      exc.j = j;
      exc.m = A -> size1;
      exc.n = A -> size2;
      throw exc;
    }
  }
  inline const double& matrix::operator()(const size_t i,  
					 const size_t j) const{
    try{
      return(*gsl_matrix_const_ptr(A,i-1,j-1));
    }
    catch(indexOutOfRange& exc){
      exc.i = i;
      exc.j = j;
      exc.m = A -> size1;
      exc.n = A -> size2;
      throw exc;
    }
  }

  inline double& vector::operator()(const size_t i) {
    try{
      return *gsl_vector_ptr(x,i-1);
    }
    catch(indexOutOfRange& exc){
      exc.i = i;
      exc.n = x -> size;
      throw exc;
    }
  }

  inline const double& vector::operator()(const size_t i) const {
    try{
      return *gsl_vector_const_ptr(x,i-1);
    }
    catch(indexOutOfRange& exc){
      exc.i = i;
      exc.n = x -> size;
      throw exc;
    }
  }
}
#endif //__LINALG_HPP__