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 description of method see [Engeln-Mullges + Uhlig, p. 92] 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> 00068 int solve_tridiag_sym(const vec_t &diag, const vec2_t &offdiag, 00069 const vec_t &b, vec_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> 00145 int solve_tridiag_nonsym(const vec_t &diag, const vec2_t &abovediag, 00146 const vec2_t &belowdiag, const vec_t &rhs, 00147 vec_t &x, size_t N) { 00148 int status; 00149 double *alpha = (double *) malloc (N * sizeof (double)); 00150 double *z = (double *) malloc (N * sizeof (double)); 00151 00152 if (alpha == 0 || z == 0) { 00153 status = GSL_ENOMEM; 00154 } else { 00155 size_t i, j; 00156 00157 /* Bidiagonalization (eliminating belowdiag) 00158 & rhs update 00159 diag' = alpha 00160 rhs' = z 00161 */ 00162 alpha[0] = O2SCL_IX(diag,0); 00163 z[0] = O2SCL_IX(rhs,0); 00164 00165 for (i = 1; i < N; i++) { 00166 const double t = O2SCL_IX(belowdiag,i-1)/alpha[i-1]; 00167 alpha[i] = O2SCL_IX(diag,i) - t*O2SCL_IX(abovediag,i-1); 00168 z[i] = O2SCL_IX(rhs,i) - t*z[i-1]; 00169 /* FIXME!!! */ 00170 if (alpha[i] == 0) { 00171 status = GSL_EZERODIV; 00172 goto solve_tridiag_nonsym_END; 00173 } 00174 } 00175 00176 /* backsubstitution */ 00177 O2SCL_IX(x,N-1) = z[N - 1]/alpha[N - 1]; 00178 if (N >= 2) { 00179 for (i = N - 2, j = 0; j <= N - 2; j++, i--) { 00180 O2SCL_IX(x,i) = (z[i] - O2SCL_IX(abovediag,i) * 00181 O2SCL_IX(x,i+1))/alpha[i]; 00182 } 00183 } 00184 00185 status = GSL_SUCCESS; 00186 } 00187 00188 solve_tridiag_nonsym_END: 00189 00190 if (z != 0) 00191 free (z); 00192 if (alpha != 0) 00193 free (alpha); 00194 00195 return status; 00196 } 00197 00198 /** 00199 \brief Solve a symmetric cyclic tridiagonal linear system 00200 00201 \todo Put reference in correctly. 00202 00203 For a description of the method see [Engeln-Mullges + Uhlig, p. 96] 00204 \verbatim 00205 * 00206 * diag[0] offdiag[0] 0 ..... offdiag[N-1] 00207 * offdiag[0] diag[1] offdiag[1] ..... 00208 * 0 offdiag[1] diag[2] 00209 * 0 0 offdiag[2] ..... 00210 * ... ... 00211 * offdiag[N-1] ... 00212 \endverbatim 00213 00214 */ 00215 template<class vec_t> 00216 int solve_cyc_tridiag_sym(const vec_t &diag, const vec_t &offdiag, 00217 const vec_t &b, vec_t &x, size_t N) { 00218 int status; 00219 double * delta = (double *) malloc (N * sizeof (double)); 00220 double * gamma = (double *) malloc (N * sizeof (double)); 00221 double * alpha = (double *) malloc (N * sizeof (double)); 00222 double * c = (double *) malloc (N * sizeof (double)); 00223 double * z = (double *) malloc (N * sizeof (double)); 00224 00225 if (delta == 0 || gamma == 0 || alpha == 0 || c == 0 || z == 0) { 00226 status = GSL_ENOMEM; 00227 } else { 00228 size_t i, j; 00229 double sum = 0.0; 00230 00231 /* factor */ 00232 00233 if (N == 1) { 00234 x[0] = b[0] / O2SCL_IX(diag,0); 00235 return GSL_SUCCESS; 00236 } 00237 00238 alpha[0] = O2SCL_IX(diag,0); 00239 gamma[0] = O2SCL_IX(offdiag,0) / alpha[0]; 00240 delta[0] = O2SCL_IX(offdiag,N-1) / alpha[0]; 00241 00242 for (i = 1; i < N - 2; i++) { 00243 alpha[i] = O2SCL_IX(diag,i) - O2SCL_IX(offdiag,i-1) * gamma[i - 1]; 00244 gamma[i] = O2SCL_IX(offdiag,i) / alpha[i]; 00245 delta[i] = -delta[i - 1] * O2SCL_IX(offdiag,i-1) / alpha[i]; 00246 } 00247 00248 for (i = 0; i < N - 2; i++) { 00249 sum += alpha[i] * delta[i] * delta[i]; 00250 } 00251 00252 alpha[N - 2] = diag[ (N - 2)] - O2SCL_IX(offdiag,N-3) * gamma[N - 3]; 00253 00254 gamma[N - 2] = (offdiag[(N - 2)] - offdiag[(N - 3)] * 00255 delta[N - 3]) / alpha[N - 2]; 00256 00257 alpha[N - 1] = diag[(N - 1)] - sum - alpha[(N - 2)] * 00258 gamma[N - 2] * gamma[N - 2]; 00259 00260 /* update */ 00261 z[0] = b[0]; 00262 for (i = 1; i < N - 1; i++) { 00263 z[i] = O2SCL_IX(b,i) - z[i - 1] * gamma[i - 1]; 00264 } 00265 sum = 0.0; 00266 for (i = 0; i < N - 2; i++) { 00267 sum += delta[i] * z[i]; 00268 } 00269 z[N - 1] = b[(N - 1)] - sum - gamma[N - 2] * z[N - 2]; 00270 for (i = 0; i < N; i++) { 00271 c[i] = z[i] / alpha[i]; 00272 } 00273 00274 /* backsubstitution */ 00275 O2SCL_IX(x,N-1) = c[N - 1]; 00276 x[(N - 2)] = c[N - 2] - gamma[N - 2] * O2SCL_IX(x,N-1); 00277 if (N >= 3) { 00278 for (i = N - 3, j = 0; j <= N - 3; j++, i--) { 00279 O2SCL_IX(x,i) = c[i] - gamma[i] * x[(i + 1)] - 00280 delta[i] * O2SCL_IX(x,N-1); 00281 } 00282 } 00283 00284 status = GSL_SUCCESS; 00285 } 00286 00287 if (z != 0) 00288 free (z); 00289 if (c != 0) 00290 free (c); 00291 if (alpha != 0) 00292 free (alpha); 00293 if (gamma != 0) 00294 free (gamma); 00295 if (delta != 0) 00296 free (delta); 00297 00298 return status; 00299 } 00300 00301 /** 00302 \brief Solve an asymmetric cyclic tridiagonal linear system 00303 00304 This function solves the following system w/o the corner 00305 elements and then use Sherman-Morrison formula to compensate for 00306 them 00307 \verbatim 00308 * 00309 * diag[0] abovediag[0] 0 ..... belowdiag[N-1] 00310 * belowdiag[0] diag[1] abovediag[1] ..... 00311 * 0 belowdiag[1] diag[2] 00312 * 0 0 belowdiag[2] ..... 00313 * ... ... 00314 * abovediag[N-1] ... 00315 \endverbatim 00316 00317 */ 00318 template<class vec_t> 00319 int solve_cyc_tridiag_nonsym(const vec_t &diag, const vec_t &abovediag, 00320 const vec_t &belowdiag, const vec_t &rhs, 00321 vec_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