1 /*
2 * Copyright (c) 2007 - 2015 Joseph Gaeddert
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining a copy
5 * of this software and associated documentation files (the "Software"), to deal
6 * in the Software without restriction, including without limitation the rights
7 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8 * copies of the Software, and to permit persons to whom the Software is
9 * furnished to do so, subject to the following conditions:
10 *
11 * The above copyright notice and this permission notice shall be included in
12 * all copies or substantial portions of the Software.
13 *
14 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20 * THE SOFTWARE.
21 */
22
23 //
24 // Solve linear system of equations using conjugate gradient method
25 //
26 // References:
27 // [Schewchuk:1994] Jonathon Richard Shewchuk, "An Introduction to
28 // the Conjugate Gradient Method Without the Agonizing Pain,"
29 // Manuscript, August, 1994.
30 //
31
32 #include <math.h>
33 #include <string.h>
34
35 #include "liquid.internal.h"
36
37 #define DEBUG_CGSOLVE 0
38
39 // solve linear system of equations using conjugate gradient method
40 // _A : symmetric positive definite matrix [size: _n x _n]
41 // _n : system dimension
42 // _b : equality [size: _n x 1]
43 // _x : solution estimate [size: _n x 1]
44 // _opts : options (ignored for now)
MATRIX(_cgsolve)45 void MATRIX(_cgsolve)(T * _A,
46 unsigned int _n,
47 T * _b,
48 T * _x,
49 void * _opts)
50 {
51 // validate input
52 if (_n == 0) {
53 fprintf(stderr,"error: matrix_cgsolve(), system dimension cannot be zero\n");
54 exit(1);
55 }
56
57 // options
58 unsigned int max_iterations = 4*_n; // maximum number of iterations
59 double tol = 1e-6; // error tolerance
60
61 unsigned int j;
62
63 // TODO : check options
64 // 1. set initial _x0
65 // 2. max number of iterations
66 // 3. residual tolerance
67
68 // allocate memory for arrays
69 T x0[_n], x1[_n]; // iterative vector x (solution estimate)
70 T d0[_n], d1[_n]; // iterative vector d
71 T r0[_n], r1[_n]; // iterative vector r (step direction)
72 T q[_n]; // A * d0
73 T Ax1[_n]; // A * x1
74
75 // scalars
76 T delta_init; // b^T * b0
77 T delta0; // r0^T * r0
78 T delta1; // r1^T * r1
79 T gamma; // d0^T * q
80 T alpha;
81 T beta;
82 double res; // residual
83 double res_opt=0.0; // residual of best solution
84
85 // initialize x0 to {0, 0, ... 0}
86 for (j=0; j<_n; j++)
87 x0[j] = 0.0;
88
89 // d0 = b - A*x0 (assume x0 = {0, 0, 0, ...0})
90 for (j=0; j<_n; j++)
91 d0[j] = _b[j];
92
93 // r0 = d0
94 memmove(r0, d0, _n*sizeof(T));
95
96 // delta_init = b^T * b
97 MATRIX(_transpose_mul)(_b, _n, 1, &delta_init);
98
99 // delta0 = r0^T * r0
100 MATRIX(_transpose_mul)(r0, _n, 1, &delta0);
101
102 // save best solution
103 memmove(_x, x0, _n*sizeof(T));
104 unsigned int i=0; // iteration counter
105 while ( (i < max_iterations) && (creal(delta0) > tol*tol*creal(delta_init)) ) {
106 #if DEBUG_CGSOLVE
107 printf("*********** %4u / %4u (max) **************\n", i, max_iterations);
108 printf(" comparing %12.4e > %12.4e\n", creal(delta0), tol*tol*creal(delta_init));
109 #endif
110
111 // q = A*d0
112 MATRIX(_mul)(_A, _n, _n,
113 d0, _n, 1,
114 q, _n, 1);
115
116 // gamma = d0^T * q
117 gamma = 0.0;
118 for (j=0; j<_n; j++) {
119 T prod = conj(d0[j]) * q[j];
120 gamma += prod;
121 }
122
123 // step size: alpha = (r0^T * r0) / (d0^T * A * d0)
124 // = delta0 / gamma
125 alpha = delta0 / gamma;
126 #if DEBUG_CGSOLVE
127 printf(" alpha = %12.8f\n", crealf(alpha));
128 printf(" delta0 = %12.8f\n", crealf(delta0));
129 #endif
130
131 // update x
132 for (j=0; j<_n; j++)
133 x1[j] = x0[j] + alpha*d0[j];
134
135 #if DEBUG_CGSOLVE
136 printf(" x:\n");
137 MATRIX(_print)(x1, _n, 1);
138 #endif
139
140 // update r
141 if ( ((i+1)%50) == 0) {
142 // peridically re-compute: r = b - A*x1
143 MATRIX(_mul)(_A, _n, _n,
144 x1, _n, 1,
145 Ax1, _n, 1);
146 for (j=0; j<_n; j++)
147 r1[j] = _b[j] - Ax1[j];
148 } else {
149 for (j=0; j<_n; j++)
150 r1[j] = r0[j] - alpha*q[j];
151 }
152
153 // delta1 = r1^T * r1
154 MATRIX(_transpose_mul)(r1, _n, 1, &delta1);
155
156 // update beta
157 beta = delta1 / delta0;
158
159 // d1 = r + beta*d0
160 for (j=0; j<_n; j++)
161 d1[j] = r1[j] + beta*d0[j];
162
163 // compute residual
164 res = sqrt( T_ABS(delta1) / T_ABS(delta_init) );
165 if (i==0 || res < res_opt) {
166 // save best solution
167 res_opt = res;
168 memmove(_x, x1, _n*sizeof(T));
169 }
170 #if DEBUG_CGSOLVE
171 printf(" res = %12.4e\n", res);
172 #endif
173
174 // copy old x, d, r, delta
175 memmove(x0, x1, _n*sizeof(T));
176 memmove(d0, d1, _n*sizeof(T));
177 memmove(r0, r1, _n*sizeof(T));
178 delta0 = delta1;
179
180 // increment counter
181 i++;
182 }
183 }
184