ViennaCL - The Vienna Computing Library  1.5.0
viennacl/linalg/cg.hpp
Go to the documentation of this file.
00001 #ifndef VIENNACL_LINALG_CG_HPP_
00002 #define VIENNACL_LINALG_CG_HPP_
00003 
00004 /* =========================================================================
00005    Copyright (c) 2010-2013, Institute for Microelectronics,
00006                             Institute for Analysis and Scientific Computing,
00007                             TU Wien.
00008    Portions of this software are copyright by UChicago Argonne, LLC.
00009 
00010                             -----------------
00011                   ViennaCL - The Vienna Computing Library
00012                             -----------------
00013 
00014    Project Head:    Karl Rupp                   rupp@iue.tuwien.ac.at
00015 
00016    (A list of authors and contributors can be found in the PDF manual)
00017 
00018    License:         MIT (X11), see file LICENSE in the base directory
00019 ============================================================================= */
00020 
00025 #include <vector>
00026 #include <map>
00027 #include <cmath>
00028 #include "viennacl/forwards.h"
00029 #include "viennacl/tools/tools.hpp"
00030 #include "viennacl/linalg/ilu.hpp"
00031 #include "viennacl/linalg/prod.hpp"
00032 #include "viennacl/linalg/inner_prod.hpp"
00033 #include "viennacl/linalg/norm_2.hpp"
00034 #include "viennacl/traits/clear.hpp"
00035 #include "viennacl/traits/size.hpp"
00036 #include "viennacl/meta/result_of.hpp"
00037 
00038 namespace viennacl
00039 {
00040   namespace linalg
00041   {
00042 
00045     class cg_tag
00046     {
00047       public:
00053         cg_tag(double tol = 1e-8, unsigned int max_iterations = 300) : tol_(tol), iterations_(max_iterations) {}
00054 
00056         double tolerance() const { return tol_; }
00058         unsigned int max_iterations() const { return iterations_; }
00059 
00061         unsigned int iters() const { return iters_taken_; }
00062         void iters(unsigned int i) const { iters_taken_ = i; }
00063 
00065         double error() const { return last_error_; }
00067         void error(double e) const { last_error_ = e; }
00068 
00069 
00070       private:
00071         double tol_;
00072         unsigned int iterations_;
00073 
00074         //return values from solver
00075         mutable unsigned int iters_taken_;
00076         mutable double last_error_;
00077     };
00078 
00079 
00089     template <typename MatrixType, typename VectorType>
00090     VectorType solve(const MatrixType & matrix, VectorType const & rhs, cg_tag const & tag)
00091     {
00092       //typedef typename VectorType::value_type      ScalarType;
00093       typedef typename viennacl::result_of::value_type<VectorType>::type        ScalarType;
00094       typedef typename viennacl::result_of::cpu_value_type<ScalarType>::type    CPU_ScalarType;
00095       //std::cout << "Starting CG" << std::endl;
00096       VectorType result = rhs;
00097       viennacl::traits::clear(result);
00098 
00099       VectorType residual = rhs;
00100       VectorType p = rhs;
00101       VectorType tmp = rhs;
00102 
00103       CPU_ScalarType ip_rr = viennacl::linalg::inner_prod(rhs,rhs);
00104       CPU_ScalarType alpha;
00105       CPU_ScalarType new_ip_rr = 0;
00106       CPU_ScalarType beta;
00107       CPU_ScalarType norm_rhs = std::sqrt(ip_rr);
00108 
00109       //std::cout << "Starting CG solver iterations... " << std::endl;
00110       if (norm_rhs == 0) //solution is zero if RHS norm is zero
00111         return result;
00112 
00113       for (unsigned int i = 0; i < tag.max_iterations(); ++i)
00114       {
00115         tag.iters(i+1);
00116         tmp = viennacl::linalg::prod(matrix, p);
00117 
00118         alpha = ip_rr / viennacl::linalg::inner_prod(tmp, p);
00119         result += alpha * p;
00120         residual -= alpha * tmp;
00121 
00122         new_ip_rr = viennacl::linalg::norm_2(residual);
00123         if (new_ip_rr / norm_rhs < tag.tolerance())
00124           break;
00125         new_ip_rr *= new_ip_rr;
00126 
00127         beta = new_ip_rr / ip_rr;
00128         ip_rr = new_ip_rr;
00129 
00130         p = residual + beta * p;
00131       }
00132 
00133       //store last error estimate:
00134       tag.error(std::sqrt(new_ip_rr) / norm_rhs);
00135 
00136       return result;
00137     }
00138 
00139     template <typename MatrixType, typename VectorType>
00140     VectorType solve(const MatrixType & matrix, VectorType const & rhs, cg_tag const & tag, viennacl::linalg::no_precond)
00141     {
00142       return solve(matrix, rhs, tag);
00143     }
00144 
00155     template <typename MatrixType, typename VectorType, typename PreconditionerType>
00156     VectorType solve(const MatrixType & matrix, VectorType const & rhs, cg_tag const & tag, PreconditionerType const & precond)
00157     {
00158       typedef typename viennacl::result_of::value_type<VectorType>::type        ScalarType;
00159       typedef typename viennacl::result_of::cpu_value_type<ScalarType>::type    CPU_ScalarType;
00160 
00161       VectorType result = rhs;
00162       viennacl::traits::clear(result);
00163 
00164       VectorType residual = rhs;
00165       VectorType tmp = rhs;
00166       VectorType z = rhs;
00167 
00168       precond.apply(z);
00169       VectorType p = z;
00170 
00171       CPU_ScalarType ip_rr = viennacl::linalg::inner_prod(residual, z);
00172       CPU_ScalarType alpha;
00173       CPU_ScalarType new_ip_rr = 0;
00174       CPU_ScalarType beta;
00175       CPU_ScalarType norm_rhs_squared = ip_rr;
00176       CPU_ScalarType new_ipp_rr_over_norm_rhs;
00177 
00178       if (norm_rhs_squared == 0) //solution is zero if RHS norm is zero
00179         return result;
00180 
00181       for (unsigned int i = 0; i < tag.max_iterations(); ++i)
00182       {
00183         tag.iters(i+1);
00184         tmp = viennacl::linalg::prod(matrix, p);
00185 
00186         alpha = ip_rr / viennacl::linalg::inner_prod(tmp, p);
00187 
00188         result += alpha * p;
00189         residual -= alpha * tmp;
00190         z = residual;
00191         precond.apply(z);
00192 
00193         new_ip_rr = viennacl::linalg::inner_prod(residual, z);
00194         new_ipp_rr_over_norm_rhs = new_ip_rr / norm_rhs_squared;
00195         if (std::fabs(new_ipp_rr_over_norm_rhs) < tag.tolerance() *  tag.tolerance())    //squared norms involved here
00196           break;
00197 
00198         beta = new_ip_rr / ip_rr;
00199         ip_rr = new_ip_rr;
00200 
00201         p = z + beta*p;
00202       }
00203 
00204       //store last error estimate:
00205       tag.error(std::sqrt(std::fabs(new_ip_rr / norm_rhs_squared)));
00206 
00207       return result;
00208     }
00209 
00210   }
00211 }
00212 
00213 #endif