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