00001 /* 00002 ------------------------------------------------------------------- 00003 00004 Copyright (C) 2006, 2007, 2008, 2009, Andrew W. Steiner 00005 00006 This file is part of O2scl. 00007 00008 O2scl is free software; you can redistribute it and/or modify 00009 it under the terms of the GNU General Public License as published by 00010 the Free Software Foundation; either version 3 of the License, or 00011 (at your option) any later version. 00012 00013 O2scl is distributed in the hope that it will be useful, 00014 but WITHOUT ANY WARRANTY; without even the implied warranty of 00015 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00016 GNU General Public License for more details. 00017 00018 You should have received a copy of the GNU General Public License 00019 along with O2scl. If not, see <http://www.gnu.org/licenses/>. 00020 00021 ------------------------------------------------------------------- 00022 */ 00023 /* linalg/tridiag.c 00024 * 00025 * Copyright (C) 1996, 1997, 1998, 1999, 2000, 2002, 2004, 00026 * 2007 Gerard Jungman, Brian Gough, David Necas 00027 * 00028 * This program is free software; you can redistribute it and/or modify 00029 * it under the terms of the GNU General Public License as published by 00030 * the Free Software Foundation; either version 3 of the License, or (at 00031 * your option) any later version. 00032 * 00033 * This program is distributed in the hope that it will be useful, but 00034 * WITHOUT ANY WARRANTY; without even the implied warranty of 00035 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 00036 * General Public License for more details. 00037 * 00038 * You should have received a copy of the GNU General Public License 00039 * along with this program; if not, write to the Free Software 00040 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 00041 * 02110-1301, USA. 00042 */ 00043 /** \file tridiag_base.h 00044 \brief File for solving tridiagonal systems 00045 */ 00046 00047 #ifdef DOXYGENP 00048 namespace o2scl_linalg { 00049 #endif 00050 00051 /** 00052 \brief Solve a symmetric tridiagonal linear system 00053 00054 \future Convert into class for memory managment and combine 00055 with other functions below 00056 00057 For a description of the method see \ref EngelnMullges96 00058 \verbatim 00059 * 00060 * diag[0] offdiag[0] 0 ..... 00061 * offdiag[0] diag[1] offdiag[1] ..... 00062 * 0 offdiag[1] diag[2] 00063 * 0 0 offdiag[2] ..... 00064 \endverbatim 00065 00066 */ 00067 template<class vec_t, class vec2_t, class vec3_t, class vec4_t> 00068 int solve_tridiag_sym(const vec_t &diag, const vec2_t &offdiag, 00069 const vec3_t &b, vec4_t &x, size_t N) { 00070 int status; 00071 double *gamma = (double *) malloc (N * sizeof (double)); 00072 double *alpha = (double *) malloc (N * sizeof (double)); 00073 double *c = (double *) malloc (N * sizeof (double)); 00074 double *z = (double *) malloc (N * sizeof (double)); 00075 00076 if (gamma == 0 || alpha == 0 || c == 0 || z == 0) { 00077 status = GSL_ENOMEM; 00078 } else { 00079 size_t i, j; 00080 00081 /* Cholesky decomposition 00082 A = L.D.L^t 00083 lower_diag(L) = gamma 00084 diag(D) = alpha 00085 */ 00086 alpha[0] = O2SCL_IX(diag,0); 00087 gamma[0] = O2SCL_IX(offdiag,0) / alpha[0]; 00088 00089 for (i = 1; i < N - 1; i++) { 00090 alpha[i] = O2SCL_IX(diag,i) - O2SCL_IX(offdiag,i-1) * gamma[i - 1]; 00091 gamma[i] = O2SCL_IX(offdiag,i) / alpha[i]; 00092 } 00093 00094 if (N > 1) { 00095 alpha[N-1]=O2SCL_IX(diag,N-1)-O2SCL_IX(offdiag,N-2)*gamma[N-2]; 00096 } 00097 00098 /* update RHS */ 00099 z[0] = b[0]; 00100 for (i = 1; i < N; i++) { 00101 z[i] = O2SCL_IX(b,i) - gamma[i - 1] * z[i - 1]; 00102 } for (i = 0; i < N; i++) { 00103 c[i] = z[i] / alpha[i]; 00104 } 00105 00106 /* backsubstitution */ 00107 O2SCL_IX(x,N-1)=c[N-1]; 00108 if (N >= 2) { 00109 for (i = N - 2, j = 0; j <= N - 2; j++, i--) { 00110 O2SCL_IX(x,i) = c[i] - gamma[i] * O2SCL_IX(x,i+1); 00111 } 00112 } 00113 00114 status = GSL_SUCCESS; 00115 } 00116 00117 if (z != 0) 00118 free (z); 00119 if (c != 0) 00120 free (c); 00121 if (alpha != 0) 00122 free (alpha); 00123 if (gamma != 0) 00124 free (gamma); 00125 00126 return status; 00127 } 00128 00129 /** 00130 \brief Solve an asymmetric tridiagonal linear system 00131 00132 This function uses plain gauss elimination, 00133 only not bothering with the zeroes. 00134 00135 \verbatim 00136 * 00137 * diag[0] abovediag[0] 0 ..... 00138 * belowdiag[0] diag[1] abovediag[1] ..... 00139 * 0 belowdiag[1] diag[2] 00140 * 0 0 belowdiag[2] ..... 00141 \endverbatim 00142 00143 */ 00144 template<class vec_t, class vec2_t, class vec3_t, class vec4_t, 00145 class vec5_t> 00146 int solve_tridiag_nonsym(const vec_t &diag, const vec2_t &abovediag, 00147 const vec3_t &belowdiag, const vec4_t &rhs, 00148 vec5_t &x, size_t N) { 00149 int status; 00150 double *alpha = (double *) malloc (N * sizeof (double)); 00151 double *z = (double *) malloc (N * sizeof (double)); 00152 00153 if (alpha == 0 || z == 0) { 00154 status = GSL_ENOMEM; 00155 } else { 00156 size_t i, j; 00157 00158 /* Bidiagonalization (eliminating belowdiag) 00159 & rhs update 00160 diag' = alpha 00161 rhs' = z 00162 */ 00163 alpha[0] = O2SCL_IX(diag,0); 00164 z[0] = O2SCL_IX(rhs,0); 00165 00166 for (i = 1; i < N; i++) { 00167 const double t = O2SCL_IX(belowdiag,i-1)/alpha[i-1]; 00168 alpha[i] = O2SCL_IX(diag,i) - t*O2SCL_IX(abovediag,i-1); 00169 z[i] = O2SCL_IX(rhs,i) - t*z[i-1]; 00170 /* FIXME!!! */ 00171 if (alpha[i] == 0) { 00172 status = GSL_EZERODIV; 00173 goto solve_tridiag_nonsym_END; 00174 } 00175 } 00176 00177 /* backsubstitution */ 00178 O2SCL_IX(x,N-1) = z[N - 1]/alpha[N - 1]; 00179 if (N >= 2) { 00180 for (i = N - 2, j = 0; j <= N - 2; j++, i--) { 00181 O2SCL_IX(x,i) = (z[i] - O2SCL_IX(abovediag,i) * 00182 O2SCL_IX(x,i+1))/alpha[i]; 00183 } 00184 } 00185 00186 status = GSL_SUCCESS; 00187 } 00188 00189 solve_tridiag_nonsym_END: 00190 00191 if (z != 0) 00192 free (z); 00193 if (alpha != 0) 00194 free (alpha); 00195 00196 return status; 00197 } 00198 00199 /** 00200 \brief Solve a symmetric cyclic tridiagonal linear system 00201 00202 For a description of the method see \ref EngelnMullges96 00203 \verbatim 00204 * 00205 * diag[0] offdiag[0] 0 ..... offdiag[N-1] 00206 * offdiag[0] diag[1] offdiag[1] ..... 00207 * 0 offdiag[1] diag[2] 00208 * 0 0 offdiag[2] ..... 00209 * ... ... 00210 * offdiag[N-1] ... 00211 \endverbatim 00212 00213 */ 00214 template<class vec_t, class vec2_t, class vec3_t, class vec4_t> 00215 int solve_cyc_tridiag_sym(const vec_t &diag, const vec2_t &offdiag, 00216 const vec3_t &b, vec4_t &x, size_t N) { 00217 int status; 00218 double * delta = (double *) malloc (N * sizeof (double)); 00219 double * gamma = (double *) malloc (N * sizeof (double)); 00220 double * alpha = (double *) malloc (N * sizeof (double)); 00221 double * c = (double *) malloc (N * sizeof (double)); 00222 double * z = (double *) malloc (N * sizeof (double)); 00223 00224 if (delta == 0 || gamma == 0 || alpha == 0 || c == 0 || z == 0) { 00225 status = GSL_ENOMEM; 00226 } else { 00227 size_t i, j; 00228 double sum = 0.0; 00229 00230 /* factor */ 00231 00232 if (N == 1) { 00233 x[0] = b[0] / O2SCL_IX(diag,0); 00234 return GSL_SUCCESS; 00235 } 00236 00237 alpha[0] = O2SCL_IX(diag,0); 00238 gamma[0] = O2SCL_IX(offdiag,0) / alpha[0]; 00239 delta[0] = O2SCL_IX(offdiag,N-1) / alpha[0]; 00240 00241 for (i = 1; i < N - 2; i++) { 00242 alpha[i] = O2SCL_IX(diag,i) - O2SCL_IX(offdiag,i-1) * gamma[i - 1]; 00243 gamma[i] = O2SCL_IX(offdiag,i) / alpha[i]; 00244 delta[i] = -delta[i - 1] * O2SCL_IX(offdiag,i-1) / alpha[i]; 00245 } 00246 00247 for (i = 0; i < N - 2; i++) { 00248 sum += alpha[i] * delta[i] * delta[i]; 00249 } 00250 00251 alpha[N - 2] = diag[ (N - 2)] - O2SCL_IX(offdiag,N-3) * gamma[N - 3]; 00252 00253 gamma[N - 2] = (offdiag[(N - 2)] - offdiag[(N - 3)] * 00254 delta[N - 3]) / alpha[N - 2]; 00255 00256 alpha[N - 1] = diag[(N - 1)] - sum - alpha[(N - 2)] * 00257 gamma[N - 2] * gamma[N - 2]; 00258 00259 /* update */ 00260 z[0] = b[0]; 00261 for (i = 1; i < N - 1; i++) { 00262 z[i] = O2SCL_IX(b,i) - z[i - 1] * gamma[i - 1]; 00263 } 00264 sum = 0.0; 00265 for (i = 0; i < N - 2; i++) { 00266 sum += delta[i] * z[i]; 00267 } 00268 z[N - 1] = b[(N - 1)] - sum - gamma[N - 2] * z[N - 2]; 00269 for (i = 0; i < N; i++) { 00270 c[i] = z[i] / alpha[i]; 00271 } 00272 00273 /* backsubstitution */ 00274 O2SCL_IX(x,N-1) = c[N - 1]; 00275 x[(N - 2)] = c[N - 2] - gamma[N - 2] * O2SCL_IX(x,N-1); 00276 if (N >= 3) { 00277 for (i = N - 3, j = 0; j <= N - 3; j++, i--) { 00278 O2SCL_IX(x,i) = c[i] - gamma[i] * x[(i + 1)] - 00279 delta[i] * O2SCL_IX(x,N-1); 00280 } 00281 } 00282 00283 status = GSL_SUCCESS; 00284 } 00285 00286 if (z != 0) 00287 free (z); 00288 if (c != 0) 00289 free (c); 00290 if (alpha != 0) 00291 free (alpha); 00292 if (gamma != 0) 00293 free (gamma); 00294 if (delta != 0) 00295 free (delta); 00296 00297 return status; 00298 } 00299 00300 /** 00301 \brief Solve an asymmetric cyclic tridiagonal linear system 00302 00303 This function solves the following system w/o the corner 00304 elements and then use Sherman-Morrison formula to compensate for 00305 them 00306 \verbatim 00307 * 00308 * diag[0] abovediag[0] 0 ..... belowdiag[N-1] 00309 * belowdiag[0] diag[1] abovediag[1] ..... 00310 * 0 belowdiag[1] diag[2] 00311 * 0 0 belowdiag[2] ..... 00312 * ... ... 00313 * abovediag[N-1] ... 00314 \endverbatim 00315 00316 */ 00317 template<class vec_t, class vec2_t, class vec3_t, class vec4_t, 00318 class vec5_t> 00319 int solve_cyc_tridiag_nonsym(const vec_t &diag, const vec2_t &abovediag, 00320 const vec3_t &belowdiag, const vec4_t &rhs, 00321 vec5_t &x, size_t N) { 00322 int status; 00323 double *alpha = (double *) malloc (N * sizeof (double)); 00324 double *zb = (double *) malloc (N * sizeof (double)); 00325 double *zu = (double *) malloc (N * sizeof (double)); 00326 double *w = (double *) malloc (N * sizeof (double)); 00327 double beta; 00328 00329 if (alpha == 0 || zb == 0 || zu == 0 || w == 0) { 00330 status = GSL_ENOMEM; 00331 } else { 00332 /* Bidiagonalization (eliminating belowdiag) 00333 & rhs update 00334 diag' = alpha 00335 rhs' = zb 00336 rhs' for Aq=u is zu 00337 */ 00338 zb[0] = O2SCL_IX(rhs,0); 00339 if (O2SCL_IX(diag,0) != 0) { 00340 beta = -O2SCL_IX(diag,0); 00341 } else { 00342 beta = 1; 00343 } 00344 const double q = 1 - O2SCL_IX(abovediag,0)*O2SCL_IX(belowdiag,0)/ 00345 (O2SCL_IX(diag,0)*diag[1]); 00346 if (fabs(q/beta) > 0.5 && fabs(q/beta) < 2) { 00347 beta *= (fabs(q/beta) < 1) ? 0.5 : 2; 00348 } 00349 zu[0] = beta; 00350 alpha[0] = O2SCL_IX(diag,0) - beta; 00351 00352 { 00353 size_t i; 00354 for (i = 1; i+1 < N; i++) { 00355 const double t = O2SCL_IX(belowdiag,i-1)/alpha[i-1]; 00356 alpha[i] = O2SCL_IX(diag,i) - t*O2SCL_IX(abovediag,i-1); 00357 zb[i] = O2SCL_IX(rhs,i) - t*zb[i-1]; 00358 zu[i] = -t*zu[i-1]; 00359 /* FIXME!!! */ 00360 if (alpha[i] == 0) { 00361 status = GSL_EZERODIV; 00362 goto solve_cyc_tridiag_nonsym_END; 00363 } 00364 } 00365 } 00366 00367 { 00368 const size_t i = N-1; 00369 const double t = O2SCL_IX(belowdiag,i-1)/alpha[i-1]; 00370 alpha[i]=O2SCL_IX(diag,i)-O2SCL_IX(abovediag,i)* 00371 O2SCL_IX(belowdiag,i)/beta- 00372 t*O2SCL_IX(abovediag,i-1); 00373 zb[i] = O2SCL_IX(rhs,i) - t*zb[i-1]; 00374 zu[i] = O2SCL_IX(abovediag,i) - t*zu[i-1]; 00375 00376 /* FIXME!!! */ 00377 if (alpha[i] == 0) { 00378 status = GSL_EZERODIV; 00379 goto solve_cyc_tridiag_nonsym_END; 00380 } 00381 } 00382 00383 { 00384 /* backsubstitution */ 00385 size_t i, j; 00386 w[N-1] = zu[N-1]/alpha[N-1]; 00387 O2SCL_IX(x,N-1) = zb[N-1]/alpha[N-1]; 00388 for (i = N - 2, j = 0; j <= N - 2; j++, i--) { 00389 w[i] = (zu[i] - O2SCL_IX(abovediag,i) * w[i+1])/alpha[i]; 00390 O2SCL_IX(x,i) = (zb[i] - O2SCL_IX(abovediag,i) * 00391 O2SCL_IX(x,i + 1))/alpha[i]; 00392 } 00393 } 00394 00395 /* Sherman-Morrison */ 00396 const double vw = w[0] + O2SCL_IX(belowdiag,N-1)/beta * w[N-1]; 00397 const double vx = O2SCL_IX(x,0) + 00398 O2SCL_IX(belowdiag,N-1)/beta * O2SCL_IX(x,N-1); 00399 00400 /* FIXME!!! */ 00401 if (vw + 1 == 0) { 00402 status = GSL_EZERODIV; 00403 goto solve_cyc_tridiag_nonsym_END; 00404 } 00405 00406 { 00407 size_t i; 00408 for (i = 0; i < N; i++) 00409 O2SCL_IX(x,i) -= vx/(1 + vw)*w[i]; 00410 } 00411 00412 status = GSL_SUCCESS; 00413 } 00414 00415 solve_cyc_tridiag_nonsym_END: 00416 00417 if (zb != 0) 00418 free (zb); 00419 if (zu != 0) 00420 free (zu); 00421 if (w != 0) 00422 free (w); 00423 if (alpha != 0) 00424 free (alpha); 00425 00426 return status; 00427 } 00428 00429 #ifdef DOXYGENP 00430 } 00431 #endif
Documentation generated with Doxygen and provided under the GNU Free Documentation License. See License Information for details.
Project hosting provided by
,
O2scl Sourceforge Project Page