1 | /* $Id$ $Revision$ */ |
2 | /* vim:set shiftwidth=4 ts=8: */ |
3 | |
4 | /************************************************************************* |
5 | * Copyright (c) 2011 AT&T Intellectual Property |
6 | * All rights reserved. This program and the accompanying materials |
7 | * are made available under the terms of the Eclipse Public License v1.0 |
8 | * which accompanies this distribution, and is available at |
9 | * http://www.eclipse.org/legal/epl-v10.html |
10 | * |
11 | * Contributors: See CVS logs. Details at http://www.graphviz.org/ |
12 | *************************************************************************/ |
13 | |
14 | #include <assert.h> |
15 | #include <string.h> |
16 | #include "sparse_solve.h" |
17 | #include "sfdpinternal.h" |
18 | #include "memory.h" |
19 | #include "logic.h" |
20 | #include "math.h" |
21 | #include "arith.h" |
22 | #include "types.h" |
23 | #include "globals.h" |
24 | |
25 | /* #define DEBUG_PRINT */ |
26 | |
27 | struct uniform_stress_matmul_data{ |
28 | real alpha; |
29 | SparseMatrix A; |
30 | }; |
31 | |
32 | |
33 | void Operator_uniform_stress_matmul_delete(Operator o){ |
34 | FREE(o->data); |
35 | } |
36 | |
37 | real *Operator_uniform_stress_matmul_apply(Operator o, real *x, real *y){ |
38 | struct uniform_stress_matmul_data *d = (struct uniform_stress_matmul_data*) (o->data); |
39 | SparseMatrix A = d->A; |
40 | real alpha = d->alpha; |
41 | real xsum = 0.; |
42 | int m = A->m, i; |
43 | |
44 | SparseMatrix_multiply_vector(A, x, &y, FALSE); |
45 | |
46 | /* alpha*V*x */ |
47 | for (i = 0; i < m; i++) xsum += x[i]; |
48 | |
49 | for (i = 0; i < m; i++) y[i] += alpha*(m*x[i] - xsum); |
50 | |
51 | return y; |
52 | } |
53 | |
54 | |
55 | |
56 | Operator Operator_uniform_stress_matmul(SparseMatrix A, real alpha){ |
57 | Operator o; |
58 | struct uniform_stress_matmul_data *d; |
59 | |
60 | o = MALLOC(sizeof(struct Operator_struct)); |
61 | o->data = d = MALLOC(sizeof(struct uniform_stress_matmul_data)); |
62 | d->alpha = alpha; |
63 | d->A = A; |
64 | o->Operator_apply = Operator_uniform_stress_matmul_apply; |
65 | return o; |
66 | } |
67 | |
68 | |
69 | real *Operator_matmul_apply(Operator o, real *x, real *y){ |
70 | SparseMatrix A = (SparseMatrix) o->data; |
71 | SparseMatrix_multiply_vector(A, x, &y, FALSE); |
72 | return y; |
73 | } |
74 | |
75 | Operator Operator_matmul_new(SparseMatrix A){ |
76 | Operator o; |
77 | |
78 | o = GNEW(struct Operator_struct); |
79 | o->data = (void*) A; |
80 | o->Operator_apply = Operator_matmul_apply; |
81 | return o; |
82 | } |
83 | |
84 | |
85 | void Operator_matmul_delete(Operator o){ |
86 | if (o) FREE(o); |
87 | } |
88 | |
89 | |
90 | real* Operator_diag_precon_apply(Operator o, real *x, real *y){ |
91 | int i, m; |
92 | real *diag = (real*) o->data; |
93 | m = (int) diag[0]; |
94 | diag++; |
95 | for (i = 0; i < m; i++) y[i] = x[i]*diag[i]; |
96 | return y; |
97 | } |
98 | |
99 | |
100 | Operator Operator_uniform_stress_diag_precon_new(SparseMatrix A, real alpha){ |
101 | Operator o; |
102 | real *diag; |
103 | int i, j, m = A->m, *ia = A->ia, *ja = A->ja; |
104 | real *a = (real*) A->a; |
105 | |
106 | assert(A->type == MATRIX_TYPE_REAL); |
107 | |
108 | assert(a); |
109 | |
110 | o = MALLOC(sizeof(struct Operator_struct)); |
111 | o->data = MALLOC(sizeof(real)*(m + 1)); |
112 | diag = (real*) o->data; |
113 | |
114 | diag[0] = m; |
115 | diag++; |
116 | for (i = 0; i < m; i++){ |
117 | diag[i] = 1./(m-1); |
118 | for (j = ia[i]; j < ia[i+1]; j++){ |
119 | if (i == ja[j] && ABS(a[j]) > 0) diag[i] = 1./((m-1)*alpha+a[j]); |
120 | } |
121 | } |
122 | |
123 | o->Operator_apply = Operator_diag_precon_apply; |
124 | |
125 | return o; |
126 | } |
127 | |
128 | |
129 | Operator Operator_diag_precon_new(SparseMatrix A){ |
130 | Operator o; |
131 | real *diag; |
132 | int i, j, m = A->m, *ia = A->ia, *ja = A->ja; |
133 | real *a = (real*) A->a; |
134 | |
135 | assert(A->type == MATRIX_TYPE_REAL); |
136 | |
137 | assert(a); |
138 | |
139 | o = N_GNEW(1,struct Operator_struct); |
140 | o->data = N_GNEW((A->m + 1),real); |
141 | diag = (real*) o->data; |
142 | |
143 | diag[0] = m; |
144 | diag++; |
145 | for (i = 0; i < m; i++){ |
146 | diag[i] = 1.; |
147 | for (j = ia[i]; j < ia[i+1]; j++){ |
148 | if (i == ja[j] && ABS(a[j]) > 0) diag[i] = 1./a[j]; |
149 | } |
150 | } |
151 | |
152 | o->Operator_apply = Operator_diag_precon_apply; |
153 | |
154 | return o; |
155 | } |
156 | |
157 | void Operator_diag_precon_delete(Operator o){ |
158 | if (o->data) FREE(o->data); |
159 | if (o) FREE(o); |
160 | } |
161 | |
162 | static real conjugate_gradient(Operator A, Operator precon, int n, real *x, real *rhs, real tol, int maxit, int *flag){ |
163 | real *z, *r, *p, *q, res = 10*tol, alpha; |
164 | real rho = 1.0e20, rho_old = 1, res0, beta; |
165 | real* (*Ax)(Operator o, real *in, real *out) = A->Operator_apply; |
166 | real* (*Minvx)(Operator o, real *in, real *out) = precon->Operator_apply; |
167 | int iter = 0; |
168 | |
169 | z = N_GNEW(n,real); |
170 | r = N_GNEW(n,real); |
171 | p = N_GNEW(n,real); |
172 | q = N_GNEW(n,real); |
173 | |
174 | r = Ax(A, x, r); |
175 | r = vector_subtract_to(n, rhs, r); |
176 | |
177 | res0 = res = sqrt(vector_product(n, r, r))/n; |
178 | #ifdef DEBUG_PRINT |
179 | if (Verbose){ |
180 | fprintf(stderr, "on entry, cg iter = %d of %d, residual = %g, tol = %g\n" , iter, maxit, res, tol); |
181 | } |
182 | #endif |
183 | |
184 | while ((iter++) < maxit && res > tol*res0){ |
185 | z = Minvx(precon, r, z); |
186 | rho = vector_product(n, r, z); |
187 | |
188 | if (iter > 1){ |
189 | beta = rho/rho_old; |
190 | p = vector_saxpy(n, z, p, beta); |
191 | } else { |
192 | MEMCPY(p, z, sizeof(real)*n); |
193 | } |
194 | |
195 | q = Ax(A, p, q); |
196 | |
197 | alpha = rho/vector_product(n, p, q); |
198 | |
199 | x = vector_saxpy2(n, x, p, alpha); |
200 | r = vector_saxpy2(n, r, q, -alpha); |
201 | |
202 | res = sqrt(vector_product(n, r, r))/n; |
203 | |
204 | #ifdef DEBUG_PRINT |
205 | if (Verbose && 0){ |
206 | fprintf(stderr, " cg iter = %d, residual = %g, relative res = %g\n" , iter, res, res/res0); |
207 | } |
208 | #endif |
209 | |
210 | |
211 | |
212 | rho_old = rho; |
213 | } |
214 | FREE(z); FREE(r); FREE(p); FREE(q); |
215 | #ifdef DEBUG |
216 | _statistics[0] += iter - 1; |
217 | #endif |
218 | |
219 | #ifdef DEBUG_PRINT |
220 | if (Verbose){ |
221 | fprintf(stderr, " cg iter = %d, residual = %g, relative res = %g\n" , iter, res, res/res0); |
222 | } |
223 | #endif |
224 | return res; |
225 | } |
226 | |
227 | real cg(Operator Ax, Operator precond, int n, int dim, real *x0, real *rhs, real tol, int maxit, int *flag){ |
228 | real *x, *b, res = 0; |
229 | int k, i; |
230 | x = N_GNEW(n, real); |
231 | b = N_GNEW(n, real); |
232 | for (k = 0; k < dim; k++){ |
233 | for (i = 0; i < n; i++) { |
234 | x[i] = x0[i*dim+k]; |
235 | b[i] = rhs[i*dim+k]; |
236 | } |
237 | |
238 | res += conjugate_gradient(Ax, precond, n, x, b, tol, maxit, flag); |
239 | for (i = 0; i < n; i++) { |
240 | rhs[i*dim+k] = x[i]; |
241 | } |
242 | } |
243 | FREE(x); |
244 | FREE(b); |
245 | return res; |
246 | |
247 | } |
248 | |
249 | real* jacobi(SparseMatrix A, int dim, real *x0, real *rhs, int maxit, int *flag){ |
250 | /* maxit iteration of jacobi */ |
251 | real *x, *y, *b, sum, diag, *a; |
252 | int k, i, j, n = A->n, *ia, *ja, iter; |
253 | x = MALLOC(sizeof(real)*n); |
254 | y = MALLOC(sizeof(real)*n); |
255 | b = MALLOC(sizeof(real)*n); |
256 | assert(A->type = MATRIX_TYPE_REAL); |
257 | ia = A->ia; ja = A->ja; a = (real*) A->a; |
258 | |
259 | for (k = 0; k < dim; k++){ |
260 | for (i = 0; i < n; i++) { |
261 | x[i] = x0[i*dim+k]; |
262 | b[i] = rhs[i*dim+k]; |
263 | } |
264 | |
265 | for (iter = 0; iter < maxit; iter++){ |
266 | for (i = 0; i < n; i++){ |
267 | sum = 0; diag = 0; |
268 | for (j = ia[i]; j < ia[i+1]; j++){ |
269 | if (ja[j] != i){ |
270 | sum += a[j]*x[ja[j]]; |
271 | } else { |
272 | diag = a[j]; |
273 | } |
274 | } |
275 | if (sum == 0) fprintf(stderr,"neighb=%d\n" ,ia[i+1]-ia[i]); |
276 | assert(diag != 0); |
277 | y[i] = (b[i] - sum)/diag; |
278 | |
279 | } |
280 | MEMCPY(x, y, sizeof(real)*n); |
281 | } |
282 | |
283 | for (i = 0; i < n; i++) { |
284 | rhs[i*dim+k] = x[i]; |
285 | } |
286 | } |
287 | |
288 | |
289 | FREE(x); |
290 | FREE(y); |
291 | FREE(b); |
292 | return rhs; |
293 | |
294 | } |
295 | |
296 | real SparseMatrix_solve(SparseMatrix A, int dim, real *x0, real *rhs, real tol, int maxit, int method, int *flag){ |
297 | Operator Ax, precond; |
298 | int n = A->m; |
299 | real res = 0; |
300 | *flag = 0; |
301 | |
302 | switch (method){ |
303 | case SOLVE_METHOD_CG: |
304 | Ax = Operator_matmul_new(A); |
305 | precond = Operator_diag_precon_new(A); |
306 | res = cg(Ax, precond, n, dim, x0, rhs, tol, maxit, flag); |
307 | Operator_matmul_delete(Ax); |
308 | Operator_diag_precon_delete(precond); |
309 | break; |
310 | case SOLVE_METHOD_JACOBI:{ |
311 | jacobi(A, dim, x0, rhs, maxit, flag); |
312 | break; |
313 | } |
314 | default: |
315 | assert(0); |
316 | break; |
317 | |
318 | } |
319 | return res; |
320 | } |
321 | |
322 | |