/src/igraph/src/linalg/arpack.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* -*- mode: C -*- */ |
2 | | /* vim:set ts=4 sw=4 sts=4 noet: */ |
3 | | /* |
4 | | IGraph library. |
5 | | Copyright (C) 2007-2012 Gabor Csardi <csardi.gabor@gmail.com> |
6 | | 334 Harvard street, Cambridge, MA 02139 USA |
7 | | |
8 | | This program is free software; you can redistribute it and/or modify |
9 | | it under the terms of the GNU General Public License as published by |
10 | | the Free Software Foundation; either version 2 of the License, or |
11 | | (at your option) any later version. |
12 | | |
13 | | This program is distributed in the hope that it will be useful, |
14 | | but WITHOUT ANY WARRANTY; without even the implied warranty of |
15 | | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
16 | | GNU General Public License for more details. |
17 | | |
18 | | You should have received a copy of the GNU General Public License |
19 | | along with this program; if not, write to the Free Software |
20 | | Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA |
21 | | 02110-1301 USA |
22 | | |
23 | | */ |
24 | | |
25 | | #include "igraph_arpack.h" |
26 | | |
27 | | #include "core/interruption.h" |
28 | | #include "linalg/arpack_internal.h" |
29 | | |
30 | | #include "igraph_memory.h" |
31 | | #include "igraph_random.h" |
32 | | |
33 | | #include <math.h> |
34 | | #include <stdio.h> |
35 | | #include <string.h> |
36 | | #include <limits.h> |
37 | | |
38 | | /* The ARPACK example file dssimp.f is used as a template */ |
39 | | |
40 | 0 | static igraph_error_t igraph_i_arpack_err_dsaupd(int error) { |
41 | 0 | switch (error) { |
42 | 0 | case 1: return IGRAPH_ARPACK_MAXIT; |
43 | 0 | case 3: return IGRAPH_ARPACK_NOSHIFT; |
44 | 0 | case -1: return IGRAPH_ARPACK_NPOS; |
45 | 0 | case -2: return IGRAPH_ARPACK_NEVNPOS; |
46 | 0 | case -3: return IGRAPH_ARPACK_NCVSMALL; |
47 | 0 | case -4: return IGRAPH_ARPACK_NONPOSI; |
48 | 0 | case -5: return IGRAPH_ARPACK_WHICHINV; |
49 | 0 | case -6: return IGRAPH_ARPACK_BMATINV; |
50 | 0 | case -7: return IGRAPH_ARPACK_WORKLSMALL; |
51 | 0 | case -8: return IGRAPH_ARPACK_TRIDERR; |
52 | 0 | case -9: return IGRAPH_ARPACK_ZEROSTART; |
53 | 0 | case -10: return IGRAPH_ARPACK_MODEINV; |
54 | 0 | case -11: return IGRAPH_ARPACK_MODEBMAT; |
55 | 0 | case -12: return IGRAPH_ARPACK_ISHIFT; |
56 | 0 | case -13: return IGRAPH_ARPACK_NEVBE; |
57 | 0 | case -9999: return IGRAPH_ARPACK_NOFACT; |
58 | 0 | default: return IGRAPH_ARPACK_UNKNOWN; |
59 | 0 | } |
60 | 0 | } |
61 | | |
62 | 0 | static igraph_error_t igraph_i_arpack_err_dseupd(int error) { |
63 | 0 | switch (error) { |
64 | 0 | case -1: return IGRAPH_ARPACK_NPOS; |
65 | 0 | case -2: return IGRAPH_ARPACK_NEVNPOS; |
66 | 0 | case -3: return IGRAPH_ARPACK_NCVSMALL; |
67 | 0 | case -5: return IGRAPH_ARPACK_WHICHINV; |
68 | 0 | case -6: return IGRAPH_ARPACK_BMATINV; |
69 | 0 | case -7: return IGRAPH_ARPACK_WORKLSMALL; |
70 | 0 | case -8: return IGRAPH_ARPACK_TRIDERR; |
71 | 0 | case -9: return IGRAPH_ARPACK_ZEROSTART; |
72 | 0 | case -10: return IGRAPH_ARPACK_MODEINV; |
73 | 0 | case -11: return IGRAPH_ARPACK_MODEBMAT; |
74 | 0 | case -12: return IGRAPH_ARPACK_NEVBE; |
75 | 0 | case -14: return IGRAPH_ARPACK_FAILED; |
76 | 0 | case -15: return IGRAPH_ARPACK_HOWMNY; |
77 | 0 | case -16: return IGRAPH_ARPACK_HOWMNYS; |
78 | 0 | case -17: return IGRAPH_ARPACK_EVDIFF; |
79 | 0 | default: return IGRAPH_ARPACK_UNKNOWN; |
80 | 0 | } |
81 | |
|
82 | 0 | } |
83 | | |
84 | 0 | static igraph_error_t igraph_i_arpack_err_dnaupd(int error) { |
85 | 0 | switch (error) { |
86 | 0 | case 1: return IGRAPH_ARPACK_MAXIT; |
87 | 0 | case 3: return IGRAPH_ARPACK_NOSHIFT; |
88 | 0 | case -1: return IGRAPH_ARPACK_NPOS; |
89 | 0 | case -2: return IGRAPH_ARPACK_NEVNPOS; |
90 | 0 | case -3: return IGRAPH_ARPACK_NCVSMALL; |
91 | 0 | case -4: return IGRAPH_ARPACK_NONPOSI; |
92 | 0 | case -5: return IGRAPH_ARPACK_WHICHINV; |
93 | 0 | case -6: return IGRAPH_ARPACK_BMATINV; |
94 | 0 | case -7: return IGRAPH_ARPACK_WORKLSMALL; |
95 | 0 | case -8: return IGRAPH_ARPACK_TRIDERR; |
96 | 0 | case -9: return IGRAPH_ARPACK_ZEROSTART; |
97 | 0 | case -10: return IGRAPH_ARPACK_MODEINV; |
98 | 0 | case -11: return IGRAPH_ARPACK_MODEBMAT; |
99 | 0 | case -12: return IGRAPH_ARPACK_ISHIFT; |
100 | 0 | case -9999: return IGRAPH_ARPACK_NOFACT; |
101 | 0 | default: return IGRAPH_ARPACK_UNKNOWN; |
102 | 0 | } |
103 | 0 | } |
104 | | |
105 | 0 | static igraph_error_t igraph_i_arpack_err_dneupd(int error) { |
106 | 0 | switch (error) { |
107 | 0 | case 1: return IGRAPH_ARPACK_REORDER; |
108 | 0 | case -1: return IGRAPH_ARPACK_NPOS; |
109 | 0 | case -2: return IGRAPH_ARPACK_NEVNPOS; |
110 | 0 | case -3: return IGRAPH_ARPACK_NCVSMALL; |
111 | 0 | case -5: return IGRAPH_ARPACK_WHICHINV; |
112 | 0 | case -6: return IGRAPH_ARPACK_BMATINV; |
113 | 0 | case -7: return IGRAPH_ARPACK_WORKLSMALL; |
114 | 0 | case -8: return IGRAPH_ARPACK_SHUR; |
115 | 0 | case -9: return IGRAPH_ARPACK_LAPACK; |
116 | 0 | case -10: return IGRAPH_ARPACK_MODEINV; |
117 | 0 | case -11: return IGRAPH_ARPACK_MODEBMAT; |
118 | 0 | case -12: return IGRAPH_ARPACK_HOWMNYS; |
119 | 0 | case -13: return IGRAPH_ARPACK_HOWMNY; |
120 | 0 | case -14: return IGRAPH_ARPACK_FAILED; |
121 | 0 | case -15: return IGRAPH_ARPACK_EVDIFF; |
122 | 0 | default: return IGRAPH_ARPACK_UNKNOWN; |
123 | 0 | } |
124 | 0 | } |
125 | | |
126 | | /* Pristine ARPACK options object that is not exposed to the user; this is used |
127 | | * as a template for \c igraph_i_arpack_options_default when the user requests |
128 | | * a pointer to the default object */ |
129 | | const static igraph_arpack_options_t igraph_i_arpack_options_pristine = { |
130 | | /* .bmat = */ { 'I' }, |
131 | | /* .n = */ 0, |
132 | | /* .which = */ { 'X', 'X' }, |
133 | | /* .nev = */ 1, |
134 | | /* .tol = */ 0, |
135 | | /* .ncv = */ 0, /* 0 means "automatic" */ |
136 | | /* .ldv = */ 0, |
137 | | /* .ishift = */ 1, |
138 | | /* .mxiter = */ 3000, |
139 | | /* .nb = */ 1, |
140 | | /* .mode = */ 1, |
141 | | /* .start = */ 0, |
142 | | /* .lworl = */ 0, |
143 | | /* .sigma = */ 0, |
144 | | /* .sigmai = */ 0, |
145 | | /* .info = */ 0, |
146 | | /* .ierr = */ 0, |
147 | | /* .noiter = */ 0, |
148 | | /* .nconv = */ 0, |
149 | | /* .numop = */ 0, |
150 | | /* .numopb = */ 0, |
151 | | /* .numreo = */ 0, |
152 | | /* .iparam = */ { |
153 | | /* same as ishift: */ 1, |
154 | | 0, |
155 | | /* same as mxiter: */ 3000, |
156 | | /* same as nb: */ 1, |
157 | | 0, |
158 | | 0, |
159 | | /* same as mode: */ 1 |
160 | | /* the rest are all zeros */ |
161 | | }, |
162 | | /* .ipntr = */ { 0 /* the rest are all zeros */ } |
163 | | }; |
164 | | |
165 | | static IGRAPH_THREAD_LOCAL igraph_arpack_options_t igraph_i_arpack_options_default; |
166 | | |
167 | | /** |
168 | | * \function igraph_arpack_options_init |
169 | | * \brief Initialize ARPACK options. |
170 | | * |
171 | | * Initializes ARPACK options, set them to default values. |
172 | | * You can always pass the initialized \ref igraph_arpack_options_t |
173 | | * object to built-in igraph functions without any modification. The |
174 | | * built-in igraph functions modify the options to perform their |
175 | | * calculation, e.g. \ref igraph_pagerank() always searches for the |
176 | | * eigenvalue with the largest magnitude, regardless of the supplied |
177 | | * value. |
178 | | * |
179 | | * </para><para> |
180 | | * If you want to implement your own function involving eigenvalue |
181 | | * calculation using ARPACK, however, you will likely need to set up |
182 | | * the fields for yourself. |
183 | | * |
184 | | * \param o The \ref igraph_arpack_options_t object to initialize. |
185 | | * |
186 | | * Time complexity: O(1). |
187 | | */ |
188 | | |
189 | 0 | void igraph_arpack_options_init(igraph_arpack_options_t *o) { |
190 | 0 | *o = igraph_i_arpack_options_pristine; |
191 | |
|
192 | 0 | o->bmat[0] = 'I'; |
193 | 0 | o->n = 0; /* needs to be updated! */ |
194 | 0 | o->which[0] = 'X'; o->which[1] = 'X'; |
195 | 0 | o->nev = 1; |
196 | 0 | o->tol = 0; |
197 | 0 | o->ncv = 0; /* 0 means "automatic" */ |
198 | 0 | o->ldv = o->n; /* will be updated to (real) n */ |
199 | 0 | o->ishift = 1; |
200 | 0 | o->mxiter = 3000; |
201 | 0 | o->nb = 1; |
202 | 0 | o->mode = 1; |
203 | 0 | o->start = 0; |
204 | 0 | o->lworkl = 0; |
205 | 0 | o->sigma = 0; |
206 | 0 | o->sigmai = 0; |
207 | 0 | o->info = o->start; |
208 | |
|
209 | 0 | o->iparam[0] = o->ishift; o->iparam[1] = 0; o->iparam[2] = o->mxiter; o->iparam[3] = o->nb; |
210 | 0 | o->iparam[4] = 0; o->iparam[5] = 0; o->iparam[6] = o->mode; o->iparam[7] = 0; |
211 | 0 | o->iparam[8] = 0; o->iparam[9] = 0; o->iparam[10] = 0; |
212 | 0 | } |
213 | | |
214 | | /** |
215 | | * \function igraph_arpack_options_get_default |
216 | | * \brief Returns a pointer to a "default" ARPACK options object. |
217 | | * |
218 | | * This function is used by other igraph functions taking an \ref igraph_arpack_options_t |
219 | | * object as an argument to get a reference to a pre-initialized "default" |
220 | | * ARPACK options object when the user passes \c NULL instead of a real ARPACK |
221 | | * options object. The object returned from this function is reset to a pristine |
222 | | * state with every call to \c igraph_arpack_options_get_default(). |
223 | | * |
224 | | * </para><para> |
225 | | * The object returned from this function must \em not be destroyed. |
226 | | * |
227 | | * Time complexity: O(1). |
228 | | */ |
229 | 0 | igraph_arpack_options_t* igraph_arpack_options_get_default(void) { |
230 | 0 | igraph_i_arpack_options_default = igraph_i_arpack_options_pristine; |
231 | 0 | return &igraph_i_arpack_options_default; |
232 | 0 | } |
233 | | |
234 | | /** |
235 | | * \function igraph_arpack_storage_init |
236 | | * \brief Initialize ARPACK storage. |
237 | | * |
238 | | * You only need this function if you want to run multiple eigenvalue |
239 | | * calculations using ARPACK, and want to spare the memory |
240 | | * allocation/deallocation between each two runs. Otherwise it is safe |
241 | | * to supply a null pointer as the \c storage argument of both \ref |
242 | | * igraph_arpack_rssolve() and \ref igraph_arpack_rnsolve() to make |
243 | | * memory allocated and deallocated automatically. |
244 | | * |
245 | | * </para><para> |
246 | | * Don't forget to call the \ref igraph_arpack_storage_destroy() |
247 | | * function on the storage object if you don't need it any more. |
248 | | * |
249 | | * \param s The \ref igraph_arpack_storage_t object to initialize. |
250 | | * \param maxn The maximum order of the matrices. |
251 | | * \param maxncv The maximum NCV parameter intended to use. |
252 | | * \param maxldv The maximum LDV parameter intended to use. |
253 | | * \param symm Whether symmetric or non-symmetric problems will be |
254 | | * solved using this \ref igraph_arpack_storage_t. (You cannot use |
255 | | * the same storage both with symmetric and non-symmetric solvers.) |
256 | | * \return Error code. |
257 | | * |
258 | | * Time complexity: O(maxncv*(maxldv+maxn)). |
259 | | */ |
260 | | |
261 | | igraph_error_t igraph_arpack_storage_init(igraph_arpack_storage_t *s, igraph_integer_t maxn, |
262 | | igraph_integer_t maxncv, igraph_integer_t maxldv, |
263 | 0 | igraph_bool_t symm) { |
264 | | |
265 | | /* TODO: check arguments */ |
266 | 0 | if (maxn > INT_MAX) { |
267 | 0 | IGRAPH_ERROR("Maximum order of matrices too large for ARPACK.", IGRAPH_EOVERFLOW); |
268 | 0 | } |
269 | 0 | if (maxncv > INT_MAX) { |
270 | 0 | IGRAPH_ERROR("Maximum NCV parameter too large for ARPACK.", IGRAPH_EOVERFLOW); |
271 | 0 | } |
272 | 0 | if (maxldv > INT_MAX) { |
273 | 0 | IGRAPH_ERROR("Maximum LDV parameter too large for ARPACK.", IGRAPH_EOVERFLOW); |
274 | 0 | } |
275 | | |
276 | 0 | s->maxn = (int) maxn; |
277 | 0 | s->maxncv = (int) maxncv; |
278 | 0 | s->maxldv = (int) maxldv; |
279 | |
|
280 | 0 | #define CHECKMEM(x) \ |
281 | 0 | if (!x) { \ |
282 | 0 | IGRAPH_ERROR("Cannot allocate memory for ARPACK", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ \ |
283 | 0 | } \ |
284 | 0 | IGRAPH_FINALLY(igraph_free, x); |
285 | |
|
286 | 0 | s->v = IGRAPH_CALLOC(maxldv * maxncv, igraph_real_t); CHECKMEM(s->v); |
287 | 0 | s->workd = IGRAPH_CALLOC(3 * maxn, igraph_real_t); CHECKMEM(s->workd); |
288 | 0 | s->d = IGRAPH_CALLOC(2 * maxncv, igraph_real_t); CHECKMEM(s->d); |
289 | 0 | s->resid = IGRAPH_CALLOC(maxn, igraph_real_t); CHECKMEM(s->resid); |
290 | 0 | s->ax = IGRAPH_CALLOC(maxn, igraph_real_t); CHECKMEM(s->ax); |
291 | 0 | s->select = IGRAPH_CALLOC(maxncv, int); CHECKMEM(s->select); |
292 | |
|
293 | 0 | if (symm) { |
294 | 0 | s->workl = IGRAPH_CALLOC(maxncv * (maxncv + 8), igraph_real_t); CHECKMEM(s->workl); |
295 | 0 | s->di = 0; |
296 | 0 | s->workev = 0; |
297 | 0 | } else { |
298 | 0 | s->workl = IGRAPH_CALLOC(3 * maxncv * (maxncv + 2), igraph_real_t); CHECKMEM(s->workl); |
299 | 0 | s->di = IGRAPH_CALLOC(2 * maxncv, igraph_real_t); CHECKMEM(s->di); |
300 | 0 | s->workev = IGRAPH_CALLOC(3 * maxncv, igraph_real_t); CHECKMEM(s->workev); |
301 | 0 | IGRAPH_FINALLY_CLEAN(2); |
302 | 0 | } |
303 | | |
304 | 0 | #undef CHECKMEM |
305 | | |
306 | 0 | IGRAPH_FINALLY_CLEAN(7); |
307 | 0 | return IGRAPH_SUCCESS; |
308 | 0 | } |
309 | | |
310 | | /** |
311 | | * \function igraph_arpack_storage_destroy |
312 | | * \brief Deallocate ARPACK storage. |
313 | | * |
314 | | * \param s The \ref igraph_arpack_storage_t object for which the |
315 | | * memory will be deallocated. |
316 | | * |
317 | | * Time complexity: operating system dependent. |
318 | | */ |
319 | | |
320 | 0 | void igraph_arpack_storage_destroy(igraph_arpack_storage_t *s) { |
321 | |
|
322 | 0 | if (s->di) { |
323 | 0 | IGRAPH_FREE(s->di); |
324 | 0 | } |
325 | 0 | if (s->workev) { |
326 | 0 | IGRAPH_FREE(s->workev); |
327 | 0 | } |
328 | |
|
329 | 0 | IGRAPH_FREE(s->workl); |
330 | 0 | IGRAPH_FREE(s->select); |
331 | 0 | IGRAPH_FREE(s->ax); |
332 | 0 | IGRAPH_FREE(s->resid); |
333 | 0 | IGRAPH_FREE(s->d); |
334 | 0 | IGRAPH_FREE(s->workd); |
335 | 0 | IGRAPH_FREE(s->v); |
336 | 0 | } |
337 | | |
338 | | /** |
339 | | * "Solver" for 1x1 eigenvalue problems since ARPACK sometimes blows up with |
340 | | * these. |
341 | | */ |
342 | | static igraph_error_t igraph_i_arpack_rssolve_1x1(igraph_arpack_function_t *fun, void *extra, |
343 | | igraph_arpack_options_t* options, |
344 | 0 | igraph_vector_t* values, igraph_matrix_t* vectors) { |
345 | 0 | igraph_real_t a, b; |
346 | 0 | int nev = options->nev; |
347 | |
|
348 | 0 | if (nev <= 0) { |
349 | 0 | IGRAPH_ERROR("ARPACK error", IGRAPH_ARPACK_NEVNPOS); |
350 | 0 | } |
351 | | |
352 | | /* Probe the value in the matrix */ |
353 | 0 | a = 1; |
354 | 0 | IGRAPH_CHECK(fun(&b, &a, 1, extra)); |
355 | | |
356 | 0 | options->nconv = nev; |
357 | |
|
358 | 0 | if (values != 0) { |
359 | 0 | IGRAPH_CHECK(igraph_vector_resize(values, 1)); |
360 | 0 | VECTOR(*values)[0] = b; |
361 | 0 | } |
362 | | |
363 | 0 | if (vectors != 0) { |
364 | 0 | IGRAPH_CHECK(igraph_matrix_resize(vectors, 1, 1)); |
365 | 0 | MATRIX(*vectors, 0, 0) = 1; |
366 | 0 | } |
367 | | |
368 | 0 | return IGRAPH_SUCCESS; |
369 | 0 | } |
370 | | |
371 | | /** |
372 | | * "Solver" for 1x1 eigenvalue problems since ARPACK sometimes blows up with |
373 | | * these. |
374 | | */ |
375 | | static igraph_error_t igraph_i_arpack_rnsolve_1x1(igraph_arpack_function_t *fun, void *extra, |
376 | | igraph_arpack_options_t* options, |
377 | 0 | igraph_matrix_t* values, igraph_matrix_t* vectors) { |
378 | 0 | igraph_real_t a, b; |
379 | 0 | int nev = options->nev; |
380 | |
|
381 | 0 | if (nev <= 0) { |
382 | 0 | IGRAPH_ERROR("ARPACK error", IGRAPH_ARPACK_NEVNPOS); |
383 | 0 | } |
384 | | |
385 | | /* Probe the value in the matrix */ |
386 | 0 | a = 1; |
387 | 0 | IGRAPH_CHECK(fun(&b, &a, 1, extra)); |
388 | | |
389 | 0 | options->nconv = nev; |
390 | |
|
391 | 0 | if (values != 0) { |
392 | 0 | IGRAPH_CHECK(igraph_matrix_resize(values, 1, 2)); |
393 | 0 | MATRIX(*values, 0, 0) = b; MATRIX(*values, 0, 1) = 0; |
394 | 0 | } |
395 | | |
396 | 0 | if (vectors != 0) { |
397 | 0 | IGRAPH_CHECK(igraph_matrix_resize(vectors, 1, 1)); |
398 | 0 | MATRIX(*vectors, 0, 0) = 1; |
399 | 0 | } |
400 | | |
401 | 0 | return IGRAPH_SUCCESS; |
402 | 0 | } |
403 | | |
404 | | /** |
405 | | * "Solver" for 2x2 nonsymmetric eigenvalue problems since ARPACK sometimes |
406 | | * blows up with these. |
407 | | */ |
408 | | static igraph_error_t igraph_i_arpack_rnsolve_2x2(igraph_arpack_function_t *fun, void *extra, |
409 | | igraph_arpack_options_t* options, igraph_matrix_t* values, |
410 | 0 | igraph_matrix_t* vectors) { |
411 | 0 | igraph_real_t vec[2], mat[4]; |
412 | 0 | igraph_real_t a, b, c, d; |
413 | 0 | igraph_real_t trace, det, tsq4_minus_d; |
414 | 0 | igraph_complex_t eval1, eval2; |
415 | 0 | igraph_complex_t evec1[2], evec2[2]; |
416 | 0 | igraph_bool_t swap_evals = false; |
417 | 0 | igraph_bool_t complex_evals = false; |
418 | 0 | int nev = options->nev; |
419 | |
|
420 | 0 | if (nev <= 0) { |
421 | 0 | IGRAPH_ERROR("ARPACK error", IGRAPH_ARPACK_NEVNPOS); |
422 | 0 | } |
423 | 0 | if (nev > 2) { |
424 | 0 | nev = 2; |
425 | 0 | } |
426 | | |
427 | | /* Probe the values in the matrix */ |
428 | 0 | vec[0] = 1; vec[1] = 0; |
429 | 0 | IGRAPH_CHECK(fun(mat, vec, 2, extra)); |
430 | 0 | vec[0] = 0; vec[1] = 1; |
431 | 0 | IGRAPH_CHECK(fun(mat + 2, vec, 2, extra)); |
432 | 0 | a = mat[0]; b = mat[2]; c = mat[1]; d = mat[3]; |
433 | | |
434 | | /* Get the trace and the determinant */ |
435 | 0 | trace = a + d; |
436 | 0 | det = a * d - b * c; |
437 | 0 | tsq4_minus_d = trace * trace / 4 - det; |
438 | | |
439 | | /* Calculate the eigenvalues */ |
440 | 0 | complex_evals = tsq4_minus_d < 0; |
441 | 0 | eval1 = igraph_complex_sqrt_real(tsq4_minus_d); |
442 | 0 | if (complex_evals) { |
443 | 0 | eval2 = igraph_complex_mul_real(eval1, -1); |
444 | 0 | } else { |
445 | | /* to avoid having -0 in the imaginary part */ |
446 | 0 | eval2 = igraph_complex(-IGRAPH_REAL(eval1), 0); |
447 | 0 | } |
448 | 0 | eval1 = igraph_complex_add_real(eval1, trace / 2); |
449 | 0 | eval2 = igraph_complex_add_real(eval2, trace / 2); |
450 | |
|
451 | 0 | if (c != 0) { |
452 | 0 | evec1[0] = igraph_complex_sub_real(eval1, d); |
453 | 0 | evec1[1] = igraph_complex(c, 0); |
454 | 0 | evec2[0] = igraph_complex_sub_real(eval2, d); |
455 | 0 | evec2[1] = igraph_complex(c, 0); |
456 | 0 | } else if (b != 0) { |
457 | 0 | evec1[0] = igraph_complex(b, 0); |
458 | 0 | evec1[1] = igraph_complex_sub_real(eval1, a); |
459 | 0 | evec2[0] = igraph_complex(b, 0); |
460 | 0 | evec2[1] = igraph_complex_sub_real(eval2, a); |
461 | 0 | } else { |
462 | 0 | evec1[0] = igraph_complex(1, 0); |
463 | 0 | evec1[1] = igraph_complex(0, 0); |
464 | 0 | evec2[0] = igraph_complex(0, 0); |
465 | 0 | evec2[1] = igraph_complex(1, 0); |
466 | 0 | } |
467 | | |
468 | | /* Sometimes we have to swap eval1 with eval2 and evec1 with eval2; |
469 | | * determine whether we have to do it now */ |
470 | 0 | if (options->which[0] == 'S') { |
471 | 0 | if (options->which[1] == 'M') { |
472 | | /* eval1 must be the one with the smallest magnitude */ |
473 | 0 | swap_evals = (igraph_complex_abs(eval1) > igraph_complex_abs(eval2)); |
474 | 0 | } else if (options->which[1] == 'R') { |
475 | | /* eval1 must be the one with the smallest real part */ |
476 | 0 | swap_evals = (IGRAPH_REAL(eval1) > IGRAPH_REAL(eval2)); |
477 | 0 | } else if (options->which[1] == 'I') { |
478 | | /* eval1 must be the one with the smallest imaginary part */ |
479 | 0 | swap_evals = (IGRAPH_IMAG(eval1) > IGRAPH_IMAG(eval2)); |
480 | 0 | } else { |
481 | 0 | IGRAPH_ERROR("ARPACK error", IGRAPH_ARPACK_WHICHINV); |
482 | 0 | } |
483 | 0 | } else if (options->which[0] == 'L') { |
484 | 0 | if (options->which[1] == 'M') { |
485 | | /* eval1 must be the one with the largest magnitude */ |
486 | 0 | swap_evals = (igraph_complex_abs(eval1) < igraph_complex_abs(eval2)); |
487 | 0 | } else if (options->which[1] == 'R') { |
488 | | /* eval1 must be the one with the largest real part */ |
489 | 0 | swap_evals = (IGRAPH_REAL(eval1) < IGRAPH_REAL(eval2)); |
490 | 0 | } else if (options->which[1] == 'I') { |
491 | | /* eval1 must be the one with the largest imaginary part */ |
492 | 0 | swap_evals = (IGRAPH_IMAG(eval1) < IGRAPH_IMAG(eval2)); |
493 | 0 | } else { |
494 | 0 | IGRAPH_ERROR("ARPACK error", IGRAPH_ARPACK_WHICHINV); |
495 | 0 | } |
496 | 0 | } else if (options->which[0] == 'X' && options->which[1] == 'X') { |
497 | | /* No preference on the ordering of eigenvectors */ |
498 | 0 | } else { |
499 | | /* fprintf(stderr, "%c%c\n", options->which[0], options->which[1]); */ |
500 | 0 | IGRAPH_ERROR("ARPACK error", IGRAPH_ARPACK_WHICHINV); |
501 | 0 | } |
502 | | |
503 | 0 | options->nconv = nev; |
504 | |
|
505 | 0 | if (swap_evals) { |
506 | 0 | igraph_complex_t dummy; |
507 | 0 | dummy = eval1; eval1 = eval2; eval2 = dummy; |
508 | 0 | dummy = evec1[0]; evec1[0] = evec2[0]; evec2[0] = dummy; |
509 | 0 | dummy = evec1[1]; evec1[1] = evec2[1]; evec2[1] = dummy; |
510 | 0 | } |
511 | |
|
512 | 0 | if (complex_evals) { |
513 | | /* The eigenvalues are conjugate pairs, so we store only the |
514 | | * one with positive imaginary part */ |
515 | 0 | if (IGRAPH_IMAG(eval1) < 0) { |
516 | 0 | eval1 = eval2; |
517 | 0 | evec1[0] = evec2[0]; evec1[1] = evec2[1]; |
518 | 0 | } |
519 | 0 | } |
520 | |
|
521 | 0 | if (values != 0) { |
522 | 0 | IGRAPH_CHECK(igraph_matrix_resize(values, nev, 2)); |
523 | 0 | MATRIX(*values, 0, 0) = IGRAPH_REAL(eval1); |
524 | 0 | MATRIX(*values, 0, 1) = IGRAPH_IMAG(eval1); |
525 | 0 | if (nev > 1) { |
526 | 0 | MATRIX(*values, 1, 0) = IGRAPH_REAL(eval2); |
527 | 0 | MATRIX(*values, 1, 1) = IGRAPH_IMAG(eval2); |
528 | 0 | } |
529 | 0 | } |
530 | | |
531 | 0 | if (vectors != 0) { |
532 | 0 | if (complex_evals) { |
533 | 0 | IGRAPH_CHECK(igraph_matrix_resize(vectors, 2, 2)); |
534 | 0 | MATRIX(*vectors, 0, 0) = IGRAPH_REAL(evec1[0]); |
535 | 0 | MATRIX(*vectors, 1, 0) = IGRAPH_REAL(evec1[1]); |
536 | 0 | MATRIX(*vectors, 0, 1) = IGRAPH_IMAG(evec1[0]); |
537 | 0 | MATRIX(*vectors, 1, 1) = IGRAPH_IMAG(evec1[1]); |
538 | 0 | } else { |
539 | 0 | IGRAPH_CHECK(igraph_matrix_resize(vectors, 2, nev)); |
540 | 0 | MATRIX(*vectors, 0, 0) = IGRAPH_REAL(evec1[0]); |
541 | 0 | MATRIX(*vectors, 1, 0) = IGRAPH_REAL(evec1[1]); |
542 | 0 | if (nev > 1) { |
543 | 0 | MATRIX(*vectors, 0, 1) = IGRAPH_REAL(evec2[0]); |
544 | 0 | MATRIX(*vectors, 1, 1) = IGRAPH_REAL(evec2[1]); |
545 | 0 | } |
546 | 0 | } |
547 | 0 | } |
548 | | |
549 | 0 | return IGRAPH_SUCCESS; |
550 | 0 | } |
551 | | |
552 | | /** |
553 | | * "Solver" for symmetric 2x2 eigenvalue problems since ARPACK sometimes blows |
554 | | * up with these. |
555 | | */ |
556 | | static igraph_error_t igraph_i_arpack_rssolve_2x2(igraph_arpack_function_t *fun, void *extra, |
557 | | igraph_arpack_options_t* options, igraph_vector_t* values, |
558 | 0 | igraph_matrix_t* vectors) { |
559 | 0 | igraph_real_t vec[2], mat[4]; |
560 | 0 | igraph_real_t a, b, c, d; |
561 | 0 | igraph_real_t trace, det, tsq4_minus_d; |
562 | 0 | igraph_real_t eval1, eval2; |
563 | 0 | int nev = options->nev; |
564 | |
|
565 | 0 | if (nev <= 0) { |
566 | 0 | IGRAPH_ERROR("ARPACK error", IGRAPH_ARPACK_NEVNPOS); |
567 | 0 | } |
568 | 0 | if (nev > 2) { |
569 | 0 | nev = 2; |
570 | 0 | } |
571 | | |
572 | | /* Probe the values in the matrix */ |
573 | 0 | vec[0] = 1; vec[1] = 0; |
574 | 0 | IGRAPH_CHECK(fun(mat, vec, 2, extra)); |
575 | 0 | vec[0] = 0; vec[1] = 1; |
576 | 0 | IGRAPH_CHECK(fun(mat + 2, vec, 2, extra)); |
577 | 0 | a = mat[0]; b = mat[2]; c = mat[1]; d = mat[3]; |
578 | | |
579 | | /* Get the trace and the determinant */ |
580 | 0 | trace = a + d; |
581 | 0 | det = a * d - b * c; |
582 | 0 | tsq4_minus_d = trace * trace / 4 - det; |
583 | |
|
584 | 0 | if (tsq4_minus_d >= 0) { |
585 | | /* Both eigenvalues are real */ |
586 | 0 | eval1 = trace / 2 + sqrt(tsq4_minus_d); |
587 | 0 | eval2 = trace / 2 - sqrt(tsq4_minus_d); |
588 | 0 | if (c != 0) { |
589 | 0 | mat[0] = eval1 - d; mat[2] = eval2 - d; |
590 | 0 | mat[1] = c; mat[3] = c; |
591 | 0 | } else if (b != 0) { |
592 | 0 | mat[0] = b; mat[2] = b; |
593 | 0 | mat[1] = eval1 - a; mat[3] = eval2 - a; |
594 | 0 | } else { |
595 | 0 | mat[0] = 1; mat[2] = 0; |
596 | 0 | mat[1] = 0; mat[3] = 1; |
597 | 0 | } |
598 | 0 | } else { |
599 | | /* Both eigenvalues are complex. Should not happen with symmetric |
600 | | * matrices. */ |
601 | 0 | IGRAPH_ERROR("ARPACK error, 2x2 matrix is not symmetric", IGRAPH_EINVAL); |
602 | 0 | } |
603 | | |
604 | | /* eval1 is always the larger eigenvalue. If we want the smaller |
605 | | * one, we have to swap eval1 with eval2 and also the columns of mat */ |
606 | 0 | if (options->which[0] == 'S') { |
607 | 0 | trace = eval1; eval1 = eval2; eval2 = trace; |
608 | 0 | trace = mat[0]; mat[0] = mat[2]; mat[2] = trace; |
609 | 0 | trace = mat[1]; mat[1] = mat[3]; mat[3] = trace; |
610 | 0 | } else if (options->which[0] == 'L' || options->which[0] == 'B') { |
611 | | /* Nothing to do here */ |
612 | 0 | } else if (options->which[0] == 'X' && options->which[1] == 'X') { |
613 | | /* No preference on the ordering of eigenvectors */ |
614 | 0 | } else { |
615 | 0 | IGRAPH_ERROR("ARPACK error", IGRAPH_ARPACK_WHICHINV); |
616 | 0 | } |
617 | | |
618 | 0 | options->nconv = nev; |
619 | |
|
620 | 0 | if (values != 0) { |
621 | 0 | IGRAPH_CHECK(igraph_vector_resize(values, nev)); |
622 | 0 | VECTOR(*values)[0] = eval1; |
623 | 0 | if (nev > 1) { |
624 | 0 | VECTOR(*values)[1] = eval2; |
625 | 0 | } |
626 | 0 | } |
627 | | |
628 | 0 | if (vectors != 0) { |
629 | 0 | IGRAPH_CHECK(igraph_matrix_resize(vectors, 2, nev)); |
630 | 0 | MATRIX(*vectors, 0, 0) = mat[0]; |
631 | 0 | MATRIX(*vectors, 1, 0) = mat[1]; |
632 | 0 | if (nev > 1) { |
633 | 0 | MATRIX(*vectors, 0, 1) = mat[2]; |
634 | 0 | MATRIX(*vectors, 1, 1) = mat[3]; |
635 | 0 | } |
636 | 0 | } |
637 | | |
638 | 0 | return IGRAPH_SUCCESS; |
639 | 0 | } |
640 | | |
641 | | igraph_error_t igraph_arpack_rssort(igraph_vector_t *values, igraph_matrix_t *vectors, |
642 | | const igraph_arpack_options_t *options, |
643 | 0 | igraph_real_t *d, const igraph_real_t *v) { |
644 | |
|
645 | 0 | igraph_vector_t order; |
646 | 0 | char sort[2]; |
647 | 0 | int apply = 1; |
648 | 0 | unsigned int n = (unsigned int) options->n; |
649 | 0 | int nconv = options->nconv; |
650 | 0 | int nev = options->nev; |
651 | 0 | unsigned int nans = (unsigned int) (nconv < nev ? nconv : nev); |
652 | 0 | unsigned int i; |
653 | |
|
654 | 0 | #define which(a,b) (options->which[0]==a && options->which[1]==b) |
655 | |
|
656 | 0 | if (which('L', 'A')) { |
657 | 0 | sort[0] = 'S'; sort[1] = 'A'; |
658 | 0 | } else if (which('S', 'A')) { |
659 | 0 | sort[0] = 'L'; sort[1] = 'A'; |
660 | 0 | } else if (which('L', 'M')) { |
661 | 0 | sort[0] = 'S'; sort[1] = 'M'; |
662 | 0 | } else if (which('S', 'M')) { |
663 | 0 | sort[0] = 'L'; sort[1] = 'M'; |
664 | 0 | } else if (which('B', 'E')) { |
665 | 0 | sort[0] = 'L'; sort[1] = 'A'; |
666 | 0 | } else { |
667 | | /* None of the above, no sorting. These 'X' values are |
668 | | * ignored by ARPACK, but we set them anyway in order to |
669 | | * avoid an uninitialized 'sort' which would trigger |
670 | | * checkers such as MemorySanitizer. */ |
671 | 0 | sort[0] = 'X'; sort[1] = 'X'; |
672 | 0 | } |
673 | |
|
674 | 0 | IGRAPH_CHECK(igraph_vector_init_range(&order, 0, nconv)); |
675 | 0 | IGRAPH_FINALLY(igraph_vector_destroy, &order); |
676 | | #ifdef HAVE_GFORTRAN |
677 | | igraphdsortr_(sort, &apply, &nconv, d, VECTOR(order), /*which_len=*/ 2); |
678 | | #else |
679 | 0 | igraphdsortr_(sort, &apply, &nconv, d, VECTOR(order)); |
680 | 0 | #endif |
681 | | |
682 | | /* BE is special */ |
683 | 0 | if (which('B', 'E')) { |
684 | 0 | int w = 0, l1 = 0, l2 = nev - 1; |
685 | 0 | igraph_vector_t order2, d2; |
686 | 0 | IGRAPH_VECTOR_INIT_FINALLY(&order2, nev); |
687 | 0 | IGRAPH_VECTOR_INIT_FINALLY(&d2, nev); |
688 | 0 | while (l1 <= l2) { |
689 | 0 | VECTOR(order2)[w] = VECTOR(order)[l1]; |
690 | 0 | VECTOR(d2)[w] = d[l1]; |
691 | 0 | w++; l1++; |
692 | 0 | if (l1 <= l2) { |
693 | 0 | VECTOR(order2)[w] = VECTOR(order)[l2]; |
694 | 0 | VECTOR(d2)[w] = d[l2]; |
695 | 0 | w++; l2--; |
696 | 0 | } |
697 | 0 | } |
698 | 0 | igraph_vector_update(&order, &order2); |
699 | 0 | igraph_vector_copy_to(&d2, d); |
700 | 0 | igraph_vector_destroy(&order2); |
701 | 0 | igraph_vector_destroy(&d2); |
702 | 0 | IGRAPH_FINALLY_CLEAN(2); |
703 | 0 | } |
704 | | |
705 | 0 | #undef which |
706 | | |
707 | | /* Copy values */ |
708 | 0 | if (values) { |
709 | 0 | IGRAPH_CHECK(igraph_vector_resize(values, nans)); |
710 | 0 | memcpy(VECTOR(*values), d, sizeof(igraph_real_t) * nans); |
711 | 0 | } |
712 | | |
713 | | /* Reorder vectors */ |
714 | 0 | if (vectors) { |
715 | 0 | IGRAPH_CHECK(igraph_matrix_resize(vectors, n, nans)); |
716 | 0 | for (i = 0; i < nans; i++) { |
717 | 0 | unsigned int idx = (unsigned int) VECTOR(order)[i]; |
718 | 0 | const igraph_real_t *ptr = v + n * idx; |
719 | 0 | memcpy(&MATRIX(*vectors, 0, i), ptr, sizeof(igraph_real_t) * n); |
720 | 0 | } |
721 | 0 | } |
722 | | |
723 | 0 | igraph_vector_destroy(&order); |
724 | 0 | IGRAPH_FINALLY_CLEAN(1); |
725 | |
|
726 | 0 | return IGRAPH_SUCCESS; |
727 | 0 | } |
728 | | |
729 | | igraph_error_t igraph_arpack_rnsort(igraph_matrix_t *values, igraph_matrix_t *vectors, |
730 | | const igraph_arpack_options_t *options, |
731 | | igraph_real_t *dr, igraph_real_t *di, |
732 | 0 | igraph_real_t *v) { |
733 | |
|
734 | 0 | igraph_vector_t order; |
735 | 0 | char sort[2]; |
736 | 0 | int apply = 1; |
737 | 0 | unsigned int n = (unsigned int) options->n; |
738 | 0 | int nconv = options->nconv; |
739 | 0 | int nev = options->nev; |
740 | 0 | unsigned int nans = (unsigned int) (nconv < nev ? nconv : nev); |
741 | 0 | unsigned int i; |
742 | |
|
743 | 0 | #define which(a,b) (options->which[0]==a && options->which[1]==b) |
744 | |
|
745 | 0 | if (which('L', 'M')) { |
746 | 0 | sort[0] = 'S'; sort[1] = 'M'; |
747 | 0 | } else if (which('S', 'M')) { |
748 | 0 | sort[0] = 'L'; sort[1] = 'M'; |
749 | 0 | } else if (which('L', 'R')) { |
750 | 0 | sort[0] = 'S'; sort[1] = 'R'; |
751 | 0 | } else if (which('S', 'R')) { |
752 | 0 | sort[0] = 'L'; sort[1] = 'R'; |
753 | 0 | } else if (which('L', 'I')) { |
754 | 0 | sort[0] = 'S'; sort[1] = 'I'; |
755 | 0 | } else if (which('S', 'I')) { |
756 | 0 | sort[0] = 'L'; sort[1] = 'I'; |
757 | 0 | } else { |
758 | | /* None of the above, no sorting. These 'X' values are |
759 | | * ignored by ARPACK, but we set them anyway in order to |
760 | | * avoid an uninitialized 'sort' which would trigger |
761 | | * checkers such as MemorySanitizer. */ |
762 | 0 | sort[0] = 'X'; sort[1] = 'X'; |
763 | 0 | } |
764 | |
|
765 | 0 | #undef which |
766 | |
|
767 | 0 | IGRAPH_CHECK(igraph_vector_init_range(&order, 0, nconv)); |
768 | 0 | IGRAPH_FINALLY(igraph_vector_destroy, &order); |
769 | | #ifdef HAVE_GFORTRAN |
770 | | igraphdsortc_(sort, &apply, &nconv, dr, di, VECTOR(order), /*which_len=*/ 2); |
771 | | #else |
772 | 0 | igraphdsortc_(sort, &apply, &nconv, dr, di, VECTOR(order)); |
773 | 0 | #endif |
774 | |
|
775 | 0 | if (values) { |
776 | 0 | IGRAPH_CHECK(igraph_matrix_resize(values, nans, 2)); |
777 | 0 | memcpy(&MATRIX(*values, 0, 0), dr, sizeof(igraph_real_t) * nans); |
778 | 0 | memcpy(&MATRIX(*values, 0, 1), di, sizeof(igraph_real_t) * nans); |
779 | 0 | } |
780 | | |
781 | 0 | if (vectors) { |
782 | 0 | int nc = 0, nr = 0, ncol, vx = 0; |
783 | 0 | for (i = 0; i < nans; i++) { |
784 | 0 | if (di[i] == 0) { |
785 | 0 | nr++; |
786 | 0 | } else { |
787 | 0 | nc++; |
788 | 0 | } |
789 | 0 | } |
790 | 0 | ncol = (nc / 2) * 2 + (nc % 2) * 2 + nr; |
791 | 0 | IGRAPH_CHECK(igraph_matrix_resize(vectors, n, ncol)); |
792 | | |
793 | 0 | for (i = 0; i < nans; i++) { |
794 | 0 | unsigned int idx; |
795 | |
|
796 | 0 | idx = (unsigned int) VECTOR(order)[i]; |
797 | |
|
798 | 0 | if (di[i] == 0) { |
799 | | /* real eigenvalue, single eigenvector */ |
800 | 0 | memcpy(&MATRIX(*vectors, 0, vx), v + n * idx, sizeof(igraph_real_t) * n); |
801 | 0 | vx++; |
802 | 0 | } else if (di[i] > 0) { |
803 | | /* complex eigenvalue, positive imaginary part encountered first. |
804 | | * ARPACK stores its eigenvector directly in two consecutive columns. |
805 | | * The complex conjugate pair of the eigenvalue (if any) will be in |
806 | | * the next column and we will skip it because we advance 'i' below */ |
807 | 0 | memcpy(&MATRIX(*vectors, 0, vx), v + n * idx, sizeof(igraph_real_t) * 2 * n); |
808 | 0 | vx += 2; |
809 | 0 | i++; |
810 | 0 | } else { |
811 | | /* complex eigenvalue, negative imaginary part encountered first. |
812 | | * The positive one will be the next one, but we need to copy the |
813 | | * eigenvector corresponding to the eigenvalue with the positive |
814 | | * imaginary part. */ |
815 | 0 | idx = (unsigned int) VECTOR(order)[i + 1]; |
816 | 0 | memcpy(&MATRIX(*vectors, 0, vx), v + n * idx, sizeof(igraph_real_t) * 2 * n); |
817 | 0 | vx += 2; |
818 | 0 | i++; |
819 | 0 | } |
820 | 0 | } |
821 | 0 | } |
822 | | |
823 | 0 | igraph_vector_destroy(&order); |
824 | 0 | IGRAPH_FINALLY_CLEAN(1); |
825 | |
|
826 | 0 | if (values) { |
827 | | /* Strive to include complex conjugate eigenvalue pairs in a way that the |
828 | | * positive imaginary part comes first */ |
829 | 0 | for (i = 0; i < nans; i++) { |
830 | 0 | if (MATRIX(*values, i, 1) == 0) { |
831 | | /* Real eigenvalue, nothing to do */ |
832 | 0 | } else if (MATRIX(*values, i, 1) < 0) { |
833 | | /* Negative imaginary part came first; negate the imaginary part for |
834 | | * this eigenvalue and the next one (which is the complex conjugate |
835 | | * pair), and skip it */ |
836 | 0 | MATRIX(*values, i, 1) *= -1; |
837 | 0 | i++; |
838 | 0 | if (i < nans) { |
839 | 0 | MATRIX(*values, i, 1) *= -1; |
840 | 0 | } |
841 | 0 | } else { |
842 | | /* Positive imaginary part; skip the next eigenvalue, which is the |
843 | | * complex conjugate pair */ |
844 | 0 | i++; |
845 | 0 | } |
846 | 0 | } |
847 | 0 | } |
848 | |
|
849 | 0 | return IGRAPH_SUCCESS; |
850 | 0 | } |
851 | | |
852 | | /** |
853 | | * \function igraph_i_arpack_auto_ncv |
854 | | * \brief Tries to set up the value of \c ncv in an \c igraph_arpack_options_t |
855 | | * automagically. |
856 | | */ |
857 | 0 | static void igraph_i_arpack_auto_ncv(igraph_arpack_options_t* options) { |
858 | | /* This is similar to how Octave determines the value of ncv, with some |
859 | | * modifications. */ |
860 | 0 | int min_ncv = options->nev * 2 + 1; |
861 | | |
862 | | /* Use twice the number of desired eigenvectors plus one by default */ |
863 | 0 | options->ncv = min_ncv; |
864 | | /* ...but use at least 20 Lanczos vectors... */ |
865 | 0 | if (options->ncv < 20) { |
866 | 0 | options->ncv = 20; |
867 | 0 | } |
868 | | /* ...but having ncv close to n leads to some problems with small graphs |
869 | | * (example: PageRank of "A <--> C, D <--> E, B"), so we don't let it |
870 | | * to be larger than n / 2... |
871 | | */ |
872 | 0 | if (options->ncv > options->n / 2) { |
873 | 0 | options->ncv = options->n / 2; |
874 | 0 | } |
875 | | /* ...but we need at least min_ncv. */ |
876 | 0 | if (options->ncv < min_ncv) { |
877 | 0 | options->ncv = min_ncv; |
878 | 0 | } |
879 | | /* ...but at most n */ |
880 | 0 | if (options->ncv > options->n) { |
881 | 0 | options->ncv = options->n; |
882 | 0 | } |
883 | 0 | } |
884 | | |
885 | | /** |
886 | | * \function igraph_i_arpack_report_no_convergence |
887 | | * \brief Prints a warning that informs the user that the ARPACK solver |
888 | | * did not converge. |
889 | | */ |
890 | 0 | static void igraph_i_arpack_report_no_convergence(const igraph_arpack_options_t* options) { |
891 | 0 | char buf[1024]; |
892 | 0 | snprintf(buf, sizeof(buf), "ARPACK solver failed to converge (%d iterations, " |
893 | 0 | "%d/%d eigenvectors converged)", options->iparam[2], |
894 | 0 | options->iparam[4], options->nev); |
895 | 0 | IGRAPH_WARNING(buf); |
896 | 0 | } |
897 | | |
898 | | /** |
899 | | * \function igraph_arpack_rssolve |
900 | | * \brief ARPACK solver for symmetric matrices. |
901 | | * |
902 | | * This is the ARPACK solver for symmetric matrices. Please use |
903 | | * \ref igraph_arpack_rnsolve() for non-symmetric matrices. |
904 | | * \param fun Pointer to an \ref igraph_arpack_function_t object, |
905 | | * the function that performs the matrix-vector multiplication. |
906 | | * \param extra An extra argument to be passed to \c fun. |
907 | | * \param options An \ref igraph_arpack_options_t object. |
908 | | * \param storage An \ref igraph_arpack_storage_t object, or a null |
909 | | * pointer. In the latter case memory allocation and deallocation |
910 | | * is performed automatically. Either this or the \p vectors argument |
911 | | * must be non-null if the ARPACK iteration is started from a |
912 | | * given starting vector. If both are given \p vectors take |
913 | | * precedence. |
914 | | * \param values If not a null pointer, then it should be a pointer to an |
915 | | * initialized vector. The eigenvalues will be stored here. The |
916 | | * vector will be resized as needed. |
917 | | * \param vectors If not a null pointer, then it must be a pointer to |
918 | | * an initialized matrix. The eigenvectors will be stored in the |
919 | | * columns of the matrix. The matrix will be resized as needed. |
920 | | * Either this or the \p storage argument must be non-null if the |
921 | | * ARPACK iteration is started from a given starting vector. If |
922 | | * both are given \p vectors take precedence. |
923 | | * \return Error code. |
924 | | * |
925 | | * Time complexity: depends on the matrix-vector |
926 | | * multiplication. Usually a small number of iterations is enough, so |
927 | | * if the matrix is sparse and the matrix-vector multiplication can be |
928 | | * done in O(n) time (the number of vertices), then the eigenvalues |
929 | | * are found in O(n) time as well. |
930 | | */ |
931 | | |
932 | | igraph_error_t igraph_arpack_rssolve(igraph_arpack_function_t *fun, void *extra, |
933 | | igraph_arpack_options_t *options, |
934 | | igraph_arpack_storage_t *storage, |
935 | 0 | igraph_vector_t *values, igraph_matrix_t *vectors) { |
936 | |
|
937 | 0 | igraph_real_t *v, *workl, *workd, *d, *resid, *ax; |
938 | 0 | igraph_bool_t free_them = false; |
939 | 0 | int *select, i; |
940 | |
|
941 | 0 | int ido = 0; |
942 | 0 | int rvec = vectors || storage ? 1 : 0; /* calculate eigenvectors? */ |
943 | 0 | char *all = "A"; |
944 | |
|
945 | 0 | int origldv = options->ldv, origlworkl = options->lworkl, |
946 | 0 | orignev = options->nev, origncv = options->ncv; |
947 | 0 | igraph_real_t origtol = options->tol; |
948 | 0 | char origwhich[2]; |
949 | |
|
950 | 0 | origwhich[0] = options->which[0]; |
951 | 0 | origwhich[1] = options->which[1]; |
952 | | |
953 | | /* Special case for 1x1 and 2x2 matrices in mode 1 */ |
954 | 0 | if (options->mode == 1 && options->n == 1) { |
955 | 0 | return igraph_i_arpack_rssolve_1x1(fun, extra, options, values, vectors); |
956 | 0 | } else if (options->mode == 1 && options->n == 2) { |
957 | 0 | return igraph_i_arpack_rssolve_2x2(fun, extra, options, values, vectors); |
958 | 0 | } |
959 | | |
960 | | /* Brush up options if needed */ |
961 | 0 | if (options->ldv == 0) { |
962 | 0 | options->ldv = options->n; |
963 | 0 | } |
964 | 0 | if (options->ncv == 0) { |
965 | 0 | igraph_i_arpack_auto_ncv(options); |
966 | 0 | } |
967 | 0 | if (options->lworkl == 0) { |
968 | 0 | options->lworkl = options->ncv * (options->ncv + 8); |
969 | 0 | } |
970 | 0 | if (options->which[0] == 'X') { |
971 | 0 | options->which[0] = 'L'; |
972 | 0 | options->which[1] = 'M'; |
973 | 0 | } |
974 | |
|
975 | 0 | if (storage) { |
976 | | /* Storage provided */ |
977 | 0 | if (storage->maxn < options->n) { |
978 | 0 | IGRAPH_ERROR("Not enough storage for ARPACK (`n')", IGRAPH_EINVAL); |
979 | 0 | } |
980 | 0 | if (storage->maxncv < options->ncv) { |
981 | 0 | IGRAPH_ERROR("Not enough storage for ARPACK (`ncv')", IGRAPH_EINVAL); |
982 | 0 | } |
983 | 0 | if (storage->maxldv < options->ldv) { |
984 | 0 | IGRAPH_ERROR("Not enough storage for ARPACK (`ldv')", IGRAPH_EINVAL); |
985 | 0 | } |
986 | | |
987 | 0 | v = storage->v; |
988 | 0 | workl = storage->workl; |
989 | 0 | workd = storage->workd; |
990 | 0 | d = storage->d; |
991 | 0 | resid = storage->resid; |
992 | 0 | ax = storage->ax; |
993 | 0 | select = storage->select; |
994 | |
|
995 | 0 | } else { |
996 | | /* Storage not provided */ |
997 | 0 | free_them = true; |
998 | |
|
999 | 0 | #define CHECKMEM(x) \ |
1000 | 0 | if (!x) { \ |
1001 | 0 | IGRAPH_ERROR("Cannot allocate memory for ARPACK", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ \ |
1002 | 0 | } \ |
1003 | 0 | IGRAPH_FINALLY(igraph_free, x); |
1004 | |
|
1005 | 0 | v = IGRAPH_CALLOC(options->ldv * options->ncv, igraph_real_t); CHECKMEM(v); |
1006 | 0 | workl = IGRAPH_CALLOC(options->lworkl, igraph_real_t); CHECKMEM(workl); |
1007 | 0 | workd = IGRAPH_CALLOC(3 * options->n, igraph_real_t); CHECKMEM(workd); |
1008 | 0 | d = IGRAPH_CALLOC(2 * options->ncv, igraph_real_t); CHECKMEM(d); |
1009 | 0 | resid = IGRAPH_CALLOC(options->n, igraph_real_t); CHECKMEM(resid); |
1010 | 0 | ax = IGRAPH_CALLOC(options->n, igraph_real_t); CHECKMEM(ax); |
1011 | 0 | select = IGRAPH_CALLOC(options->ncv, int); CHECKMEM(select); |
1012 | |
|
1013 | 0 | #undef CHECKMEM |
1014 | |
|
1015 | 0 | } |
1016 | | |
1017 | | /* Set final bits */ |
1018 | 0 | options->bmat[0] = 'I'; |
1019 | 0 | options->iparam[0] = options->ishift; |
1020 | 0 | options->iparam[1] = 0; // not referenced |
1021 | 0 | options->iparam[2] = options->mxiter; |
1022 | 0 | options->iparam[3] = 1; // currently dsaupd() works only for nb=1 |
1023 | 0 | options->iparam[4] = 0; |
1024 | 0 | options->iparam[5] = 0; // not referenced |
1025 | 0 | options->iparam[6] = options->mode; |
1026 | 0 | options->iparam[7] = 0; // return value |
1027 | 0 | options->iparam[8] = 0; // return value |
1028 | 0 | options->iparam[9] = 0; // return value |
1029 | 0 | options->iparam[10] = 0; // return value |
1030 | 0 | options->info = 1; // always use a provided starting vector |
1031 | 0 | if (options->start) { |
1032 | | // user provided the starting vector so we just use that |
1033 | 0 | if (!storage && !vectors) { |
1034 | 0 | IGRAPH_ERROR("Starting vector not given", IGRAPH_EINVAL); |
1035 | 0 | } |
1036 | 0 | if (vectors && (igraph_matrix_nrow(vectors) != options->n || |
1037 | 0 | igraph_matrix_ncol(vectors) < 1)) { |
1038 | 0 | IGRAPH_ERROR("Invalid starting vector size", IGRAPH_EINVAL); |
1039 | 0 | } |
1040 | 0 | if (vectors) { |
1041 | 0 | for (i = 0; i < options->n; i++) { |
1042 | 0 | resid[i] = MATRIX(*vectors, i, 0); |
1043 | 0 | } |
1044 | 0 | } |
1045 | 0 | } else { |
1046 | | // we need to generate a random vector on our own; let's not rely on |
1047 | | // ARPACK to do so because we want to use our own RNG |
1048 | 0 | RNG_BEGIN(); |
1049 | 0 | for (i = 0; i < options->n; i++) { |
1050 | 0 | resid[i] = RNG_UNIF(-1, 1); |
1051 | 0 | } |
1052 | 0 | RNG_END(); |
1053 | 0 | } |
1054 | | |
1055 | | /* Ok, we have everything */ |
1056 | 0 | while (1) { |
1057 | 0 | igraph_real_t *from, *to; |
1058 | |
|
1059 | 0 | IGRAPH_ALLOW_INTERRUPTION(); |
1060 | | |
1061 | | #ifdef HAVE_GFORTRAN |
1062 | | igraphdsaupd_(&ido, options->bmat, &options->n, options->which, |
1063 | | &options->nev, &options->tol, |
1064 | | resid, &options->ncv, v, &options->ldv, |
1065 | | options->iparam, options->ipntr, |
1066 | | workd, workl, &options->lworkl, &options->info, |
1067 | | /*bmat_len=*/ 1, /*which_len=*/ 2); |
1068 | | #else |
1069 | 0 | igraphdsaupd_(&ido, options->bmat, &options->n, options->which, |
1070 | 0 | &options->nev, &options->tol, |
1071 | 0 | resid, &options->ncv, v, &options->ldv, |
1072 | 0 | options->iparam, options->ipntr, |
1073 | 0 | workd, workl, &options->lworkl, &options->info); |
1074 | 0 | #endif |
1075 | | /* When there is a non-zero error code in options->info, we expect that |
1076 | | * ARPACK requests a termination of the iteration by setting ido=99. */ |
1077 | 0 | IGRAPH_ASSERT(ido == 99 || options->info == 0); |
1078 | | |
1079 | 0 | if (ido == -1 || ido == 1) { |
1080 | 0 | from = workd + options->ipntr[0] - 1; |
1081 | 0 | to = workd + options->ipntr[1] - 1; |
1082 | 0 | IGRAPH_CHECK(fun(to, from, options->n, extra)); |
1083 | 0 | } else if (ido == 2) { |
1084 | 0 | from = workd + options->ipntr[0] - 1; |
1085 | 0 | to = workd + options->ipntr[1] - 1; |
1086 | 0 | memcpy(to, from, sizeof(igraph_real_t) * options->n); |
1087 | 0 | } else if (ido == 99) { |
1088 | 0 | break; |
1089 | 0 | } else { |
1090 | 0 | IGRAPH_ERRORF("Unexpected IDO value %d when running ARPACK.", IGRAPH_FAILURE, ido); |
1091 | 0 | } |
1092 | 0 | } |
1093 | | |
1094 | 0 | if (options->info == 1) { |
1095 | 0 | igraph_i_arpack_report_no_convergence(options); |
1096 | 0 | } |
1097 | 0 | if (options->info != 0) { |
1098 | 0 | IGRAPH_ERROR("ARPACK error", igraph_i_arpack_err_dsaupd(options->info)); |
1099 | 0 | } |
1100 | | |
1101 | 0 | options->ierr = 0; |
1102 | | #ifdef HAVE_GFORTRAN |
1103 | | igraphdseupd_(&rvec, all, select, d, v, &options->ldv, |
1104 | | &options->sigma, options->bmat, &options->n, |
1105 | | options->which, &options->nev, &options->tol, |
1106 | | resid, &options->ncv, v, &options->ldv, options->iparam, |
1107 | | options->ipntr, workd, workl, &options->lworkl, |
1108 | | &options->ierr, /*howmny_len=*/ 1, /*bmat_len=*/ 1, |
1109 | | /*which_len=*/ 2); |
1110 | | #else |
1111 | 0 | igraphdseupd_(&rvec, all, select, d, v, &options->ldv, |
1112 | 0 | &options->sigma, options->bmat, &options->n, |
1113 | 0 | options->which, &options->nev, &options->tol, |
1114 | 0 | resid, &options->ncv, v, &options->ldv, options->iparam, |
1115 | 0 | options->ipntr, workd, workl, &options->lworkl, |
1116 | 0 | &options->ierr); |
1117 | 0 | #endif |
1118 | |
|
1119 | 0 | if (options->ierr != 0) { |
1120 | 0 | IGRAPH_ERROR("ARPACK error", igraph_i_arpack_err_dseupd(options->ierr)); |
1121 | 0 | } |
1122 | | |
1123 | | /* Save the result */ |
1124 | | |
1125 | 0 | options->noiter = options->iparam[2]; |
1126 | 0 | options->nconv = options->iparam[4]; |
1127 | 0 | options->numop = options->iparam[8]; |
1128 | 0 | options->numopb = options->iparam[9]; |
1129 | 0 | options->numreo = options->iparam[10]; |
1130 | |
|
1131 | 0 | if (options->nconv < options->nev) { |
1132 | 0 | IGRAPH_WARNING("Not enough eigenvalues/vectors in symmetric ARPACK " |
1133 | 0 | "solver"); |
1134 | 0 | } |
1135 | |
|
1136 | 0 | if (values || vectors) { |
1137 | 0 | IGRAPH_CHECK(igraph_arpack_rssort(values, vectors, options, d, v)); |
1138 | 0 | } |
1139 | | |
1140 | 0 | options->ldv = origldv; |
1141 | 0 | options->ncv = origncv; |
1142 | 0 | options->lworkl = origlworkl; |
1143 | 0 | options->which[0] = origwhich[0]; options->which[1] = origwhich[1]; |
1144 | 0 | options->tol = origtol; |
1145 | 0 | options->nev = orignev; |
1146 | | |
1147 | | /* Clean up if needed */ |
1148 | 0 | if (free_them) { |
1149 | 0 | IGRAPH_FREE(select); |
1150 | 0 | IGRAPH_FREE(ax); |
1151 | 0 | IGRAPH_FREE(resid); |
1152 | 0 | IGRAPH_FREE(d); |
1153 | 0 | IGRAPH_FREE(workd); |
1154 | 0 | IGRAPH_FREE(workl); |
1155 | 0 | IGRAPH_FREE(v); |
1156 | 0 | IGRAPH_FINALLY_CLEAN(7); |
1157 | 0 | } |
1158 | 0 | return IGRAPH_SUCCESS; |
1159 | 0 | } |
1160 | | |
1161 | | /** |
1162 | | * \function igraph_arpack_rnsolve |
1163 | | * \brief ARPACK solver for non-symmetric matrices. |
1164 | | * |
1165 | | * Please always consider calling \ref igraph_arpack_rssolve() if your |
1166 | | * matrix is symmetric, it is much faster. |
1167 | | * \ref igraph_arpack_rnsolve() for non-symmetric matrices. |
1168 | | * </para><para> |
1169 | | * Note that ARPACK is not called for 2x2 matrices as an exact algebraic |
1170 | | * solution exists in these cases. |
1171 | | * |
1172 | | * \param fun Pointer to an \ref igraph_arpack_function_t object, |
1173 | | * the function that performs the matrix-vector multiplication. |
1174 | | * \param extra An extra argument to be passed to \c fun. |
1175 | | * \param options An \ref igraph_arpack_options_t object. |
1176 | | * \param storage An \ref igraph_arpack_storage_t object, or a null |
1177 | | * pointer. In the latter case memory allocation and deallocation |
1178 | | * is performed automatically. |
1179 | | * \param values If not a null pointer, then it should be a pointer to an |
1180 | | * initialized matrix. The (possibly complex) eigenvalues will be |
1181 | | * stored here. The matrix will have two columns, the first column |
1182 | | * contains the real, the second the imaginary parts of the |
1183 | | * eigenvalues. |
1184 | | * The matrix will be resized as needed. |
1185 | | * \param vectors If not a null pointer, then it must be a pointer to |
1186 | | * an initialized matrix. The eigenvectors will be stored in the |
1187 | | * columns of the matrix. The matrix will be resized as needed. |
1188 | | * Note that real eigenvalues will have real eigenvectors in a single |
1189 | | * column in this matrix; however, complex eigenvalues come in conjugate |
1190 | | * pairs and the result matrix will store the eigenvector corresponding to |
1191 | | * the eigenvalue with \em positive imaginary part only. Since in this case |
1192 | | * the eigenvector is also complex, it will occupy \em two columns in the |
1193 | | * eigenvector matrix (the real and the imaginary parts, in this order). |
1194 | | * Caveat: if the eigenvalue vector returns only the eigenvalue with the |
1195 | | * \em negative imaginary part for a complex conjugate eigenvalue pair, the |
1196 | | * result vector will \em still store the eigenvector corresponding to the |
1197 | | * eigenvalue with the positive imaginary part (since this is how ARPACK |
1198 | | * works). |
1199 | | * \return Error code. |
1200 | | * |
1201 | | * Time complexity: depends on the matrix-vector |
1202 | | * multiplication. Usually a small number of iterations is enough, so |
1203 | | * if the matrix is sparse and the matrix-vector multiplication can be |
1204 | | * done in O(n) time (the number of vertices), then the eigenvalues |
1205 | | * are found in O(n) time as well. |
1206 | | */ |
1207 | | |
1208 | | igraph_error_t igraph_arpack_rnsolve(igraph_arpack_function_t *fun, void *extra, |
1209 | | igraph_arpack_options_t *options, |
1210 | | igraph_arpack_storage_t *storage, |
1211 | 0 | igraph_matrix_t *values, igraph_matrix_t *vectors) { |
1212 | |
|
1213 | 0 | igraph_real_t *v, *workl, *workd, *dr, *di, *resid, *workev; |
1214 | 0 | igraph_bool_t free_them = false; |
1215 | 0 | int *select, i; |
1216 | |
|
1217 | 0 | int ido = 0; |
1218 | 0 | int rvec = vectors || storage ? 1 : 0; |
1219 | 0 | char *all = "A"; |
1220 | |
|
1221 | 0 | int origldv = options->ldv, origlworkl = options->lworkl, |
1222 | 0 | orignev = options->nev, origncv = options->ncv; |
1223 | 0 | igraph_real_t origtol = options->tol; |
1224 | 0 | int d_size; |
1225 | 0 | char origwhich[2]; |
1226 | |
|
1227 | 0 | origwhich[0] = options->which[0]; |
1228 | 0 | origwhich[1] = options->which[1]; |
1229 | | |
1230 | | /* Special case for 1x1 and 2x2 matrices in mode 1 */ |
1231 | 0 | if (options->mode == 1 && options->n == 1) { |
1232 | 0 | return igraph_i_arpack_rnsolve_1x1(fun, extra, options, values, vectors); |
1233 | 0 | } else if (options->mode == 1 && options->n == 2) { |
1234 | 0 | return igraph_i_arpack_rnsolve_2x2(fun, extra, options, values, vectors); |
1235 | 0 | } |
1236 | | |
1237 | | /* Brush up options if needed */ |
1238 | 0 | if (options->ldv == 0) { |
1239 | 0 | options->ldv = options->n; |
1240 | 0 | } |
1241 | 0 | if (options->ncv == 0) { |
1242 | 0 | igraph_i_arpack_auto_ncv(options); |
1243 | 0 | } |
1244 | 0 | if (options->lworkl == 0) { |
1245 | 0 | options->lworkl = 3 * options->ncv * (options->ncv + 2); |
1246 | 0 | } |
1247 | 0 | if (options->which[0] == 'X') { |
1248 | 0 | options->which[0] = 'L'; |
1249 | 0 | options->which[1] = 'M'; |
1250 | 0 | } |
1251 | |
|
1252 | 0 | if (storage) { |
1253 | | /* Storage provided */ |
1254 | 0 | if (storage->maxn < options->n) { |
1255 | 0 | IGRAPH_ERROR("Not enough storage for ARPACK (`n')", IGRAPH_EINVAL); |
1256 | 0 | } |
1257 | 0 | if (storage->maxncv < options->ncv) { |
1258 | 0 | IGRAPH_ERROR("Not enough storage for ARPACK (`ncv')", IGRAPH_EINVAL); |
1259 | 0 | } |
1260 | 0 | if (storage->maxldv < options->ldv) { |
1261 | 0 | IGRAPH_ERROR("Not enough storage for ARPACK (`ldv')", IGRAPH_EINVAL); |
1262 | 0 | } |
1263 | | |
1264 | 0 | v = storage->v; |
1265 | 0 | workl = storage->workl; |
1266 | 0 | workd = storage->workd; |
1267 | 0 | workev = storage->workev; |
1268 | 0 | dr = storage->d; |
1269 | 0 | di = storage->di; |
1270 | 0 | d_size = options->n; |
1271 | 0 | resid = storage->resid; |
1272 | 0 | select = storage->select; |
1273 | |
|
1274 | 0 | } else { |
1275 | | /* Storage not provided */ |
1276 | 0 | free_them = true; |
1277 | |
|
1278 | 0 | #define CHECKMEM(x) \ |
1279 | 0 | if (!x) { \ |
1280 | 0 | IGRAPH_ERROR("Cannot allocate memory for ARPACK", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ \ |
1281 | 0 | } \ |
1282 | 0 | IGRAPH_FINALLY(igraph_free, x); |
1283 | |
|
1284 | 0 | v = IGRAPH_CALLOC(options->n * options->ncv, igraph_real_t); CHECKMEM(v); |
1285 | 0 | workl = IGRAPH_CALLOC(options->lworkl, igraph_real_t); CHECKMEM(workl); |
1286 | 0 | workd = IGRAPH_CALLOC(3 * options->n, igraph_real_t); CHECKMEM(workd); |
1287 | 0 | d_size = 2 * options->nev + 1 > options->ncv ? 2 * options->nev + 1 : options->ncv; |
1288 | 0 | dr = IGRAPH_CALLOC(d_size, igraph_real_t); CHECKMEM(dr); |
1289 | 0 | di = IGRAPH_CALLOC(d_size, igraph_real_t); CHECKMEM(di); |
1290 | 0 | resid = IGRAPH_CALLOC(options->n, igraph_real_t); CHECKMEM(resid); |
1291 | 0 | select = IGRAPH_CALLOC(options->ncv, int); CHECKMEM(select); |
1292 | 0 | workev = IGRAPH_CALLOC(3 * options->ncv, igraph_real_t); CHECKMEM(workev); |
1293 | |
|
1294 | 0 | #undef CHECKMEM |
1295 | |
|
1296 | 0 | } |
1297 | | |
1298 | | /* Set final bits */ |
1299 | 0 | options->bmat[0] = 'I'; |
1300 | 0 | options->iparam[0] = options->ishift; |
1301 | 0 | options->iparam[1] = 0; // not referenced |
1302 | 0 | options->iparam[2] = options->mxiter; |
1303 | 0 | options->iparam[3] = 1; // currently dnaupd() works only for nb=1 |
1304 | 0 | options->iparam[4] = 0; |
1305 | 0 | options->iparam[5] = 0; // not referenced |
1306 | 0 | options->iparam[6] = options->mode; |
1307 | 0 | options->iparam[7] = 0; // return value |
1308 | 0 | options->iparam[8] = 0; // return value |
1309 | 0 | options->iparam[9] = 0; // return value |
1310 | 0 | options->iparam[10] = 0; // return value |
1311 | 0 | options->info = 1; // always use a provided starting vector |
1312 | 0 | if (options->start) { |
1313 | 0 | if (!storage && !vectors) { |
1314 | 0 | IGRAPH_ERROR("Starting vector not given", IGRAPH_EINVAL); |
1315 | 0 | } |
1316 | 0 | if (vectors && (igraph_matrix_nrow(vectors) != options->n || |
1317 | 0 | igraph_matrix_ncol(vectors) != 1)) { |
1318 | 0 | IGRAPH_ERROR("Invalid starting vector size", IGRAPH_EINVAL); |
1319 | 0 | } |
1320 | 0 | if (vectors) { |
1321 | 0 | for (i = 0; i < options->n; i++) { |
1322 | 0 | resid[i] = MATRIX(*vectors, i, 0); |
1323 | 0 | } |
1324 | 0 | } |
1325 | 0 | } else { |
1326 | | // we need to generate a random vector on our own; let's not rely on |
1327 | | // ARPACK to do so because we want to use our own RNG |
1328 | 0 | RNG_BEGIN(); |
1329 | 0 | for (i = 0; i < options->n; i++) { |
1330 | 0 | resid[i] = RNG_UNIF(-1, 1); |
1331 | 0 | } |
1332 | 0 | RNG_END(); |
1333 | 0 | } |
1334 | | |
1335 | | /* Ok, we have everything */ |
1336 | 0 | while (1) { |
1337 | 0 | igraph_real_t *from, *to; |
1338 | |
|
1339 | 0 | IGRAPH_ALLOW_INTERRUPTION(); |
1340 | | |
1341 | | #ifdef HAVE_GFORTRAN |
1342 | | igraphdnaupd_(&ido, options->bmat, &options->n, options->which, |
1343 | | &options->nev, &options->tol, |
1344 | | resid, &options->ncv, v, &options->ldv, |
1345 | | options->iparam, options->ipntr, |
1346 | | workd, workl, &options->lworkl, &options->info, |
1347 | | /*bmat_len=*/ 1, /*which_len=*/ 2); |
1348 | | #else |
1349 | 0 | igraphdnaupd_(&ido, options->bmat, &options->n, options->which, |
1350 | 0 | &options->nev, &options->tol, |
1351 | 0 | resid, &options->ncv, v, &options->ldv, |
1352 | 0 | options->iparam, options->ipntr, |
1353 | 0 | workd, workl, &options->lworkl, &options->info); |
1354 | 0 | #endif |
1355 | | /* When there is a non-zero error code in options->info, we expect that |
1356 | | * ARPACK requests a termination of the iteration by setting ido=99. */ |
1357 | 0 | IGRAPH_ASSERT(ido == 99 || options->info == 0); |
1358 | | |
1359 | 0 | if (ido == -1 || ido == 1) { |
1360 | 0 | from = workd + options->ipntr[0] - 1; |
1361 | 0 | to = workd + options->ipntr[1] - 1; |
1362 | 0 | IGRAPH_CHECK(fun(to, from, options->n, extra)); |
1363 | 0 | } else if (ido == 2) { |
1364 | 0 | from = workd + options->ipntr[0] - 1; |
1365 | 0 | to = workd + options->ipntr[1] - 1; |
1366 | 0 | memcpy(to, from, sizeof(igraph_real_t) * options->n); |
1367 | 0 | } else if (ido == 4) { |
1368 | | /* same as ido == 1 but the arguments are at different places */ |
1369 | 0 | from = workd + options->ipntr[0] - 1; |
1370 | 0 | to = workd + options->ipntr[2] - 1; |
1371 | 0 | IGRAPH_CHECK(fun(to, from, options->n, extra)); |
1372 | 0 | } else if (ido == 99) { |
1373 | 0 | break; |
1374 | 0 | } else { |
1375 | 0 | IGRAPH_ERRORF("Unexpected IDO value %d when running ARPACK.", IGRAPH_FAILURE, ido); |
1376 | 0 | } |
1377 | 0 | } |
1378 | | |
1379 | 0 | if (options->info == 1) { |
1380 | 0 | igraph_i_arpack_report_no_convergence(options); |
1381 | 0 | } |
1382 | 0 | if (options->info != 0 && options->info != -9999) { |
1383 | 0 | IGRAPH_ERROR("ARPACK error", igraph_i_arpack_err_dnaupd(options->info)); |
1384 | 0 | } |
1385 | | |
1386 | 0 | options->ierr = 0; |
1387 | | #ifdef HAVE_GFORTRAN |
1388 | | igraphdneupd_(&rvec, all, select, dr, di, v, &options->ldv, |
1389 | | &options->sigma, &options->sigmai, workev, options->bmat, |
1390 | | &options->n, options->which, &options->nev, &options->tol, |
1391 | | resid, &options->ncv, v, &options->ldv, options->iparam, |
1392 | | options->ipntr, workd, workl, &options->lworkl, |
1393 | | &options->ierr, /*howmny_len=*/ 1, /*bmat_len=*/ 1, |
1394 | | /*which_len=*/ 2); |
1395 | | #else |
1396 | 0 | igraphdneupd_(&rvec, all, select, dr, di, v, &options->ldv, |
1397 | 0 | &options->sigma, &options->sigmai, workev, options->bmat, |
1398 | 0 | &options->n, options->which, &options->nev, &options->tol, |
1399 | 0 | resid, &options->ncv, v, &options->ldv, options->iparam, |
1400 | 0 | options->ipntr, workd, workl, &options->lworkl, |
1401 | 0 | &options->ierr); |
1402 | 0 | #endif |
1403 | |
|
1404 | 0 | if (options->ierr != 0) { |
1405 | 0 | IGRAPH_ERROR("ARPACK error", igraph_i_arpack_err_dneupd(options->info)); |
1406 | 0 | } |
1407 | | |
1408 | | /* Save the result */ |
1409 | | |
1410 | 0 | options->noiter = options->iparam[2]; |
1411 | 0 | options->nconv = options->iparam[4]; |
1412 | 0 | options->numop = options->iparam[8]; |
1413 | 0 | options->numopb = options->iparam[9]; |
1414 | 0 | options->numreo = options->iparam[10]; |
1415 | |
|
1416 | 0 | if (options->nconv < options->nev) { |
1417 | 0 | IGRAPH_WARNING("Not enough eigenvalues/vectors in ARPACK " |
1418 | 0 | "solver"); |
1419 | 0 | } |
1420 | | |
1421 | | /* ARPACK might modify stuff in 'options' so reset everything that could |
1422 | | * potentially get modified */ |
1423 | 0 | options->ldv = origldv; |
1424 | 0 | options->ncv = origncv; |
1425 | 0 | options->lworkl = origlworkl; |
1426 | 0 | options->which[0] = origwhich[0]; options->which[1] = origwhich[1]; |
1427 | 0 | options->tol = origtol; |
1428 | 0 | options->nev = orignev; |
1429 | |
|
1430 | 0 | if (values || vectors) { |
1431 | 0 | IGRAPH_CHECK(igraph_arpack_rnsort(values, vectors, options, |
1432 | 0 | dr, di, v)); |
1433 | 0 | } |
1434 | | |
1435 | | /* Clean up if needed */ |
1436 | 0 | if (free_them) { |
1437 | 0 | IGRAPH_FREE(workev); |
1438 | 0 | IGRAPH_FREE(select); |
1439 | 0 | IGRAPH_FREE(resid); |
1440 | 0 | IGRAPH_FREE(di); |
1441 | 0 | IGRAPH_FREE(dr); |
1442 | 0 | IGRAPH_FREE(workd); |
1443 | 0 | IGRAPH_FREE(workl); |
1444 | 0 | IGRAPH_FREE(v); |
1445 | 0 | IGRAPH_FINALLY_CLEAN(8); |
1446 | 0 | } |
1447 | 0 | return IGRAPH_SUCCESS; |
1448 | 0 | } |
1449 | | |
1450 | | /** |
1451 | | * \function igraph_arpack_unpack_complex |
1452 | | * \brief Makes the result of the non-symmetric ARPACK solver more readable. |
1453 | | * |
1454 | | * This function works on the output of \ref igraph_arpack_rnsolve and |
1455 | | * brushes it up a bit: it only keeps \p nev eigenvalues/vectors and |
1456 | | * every eigenvector is stored in two columns of the \p vectors |
1457 | | * matrix. |
1458 | | * |
1459 | | * </para><para> |
1460 | | * The output of the non-symmetric ARPACK solver is somewhat hard to |
1461 | | * parse, as real eigenvectors occupy only one column in the matrix, |
1462 | | * and the complex conjugate eigenvectors are not stored at all |
1463 | | * (usually). The other problem is that the solver might return more |
1464 | | * eigenvalues than requested. The common use of this function is to |
1465 | | * call it directly after \ref igraph_arpack_rnsolve with its \p |
1466 | | * vectors and \p values argument and \c options->nev as \p nev. |
1467 | | * This will add the vectors for eigenvalues with a negative imaginary |
1468 | | * part and return all vectors as 2 columns, a real and imaginary part. |
1469 | | * \param vectors The eigenvector matrix, as returned by \ref |
1470 | | * igraph_arpack_rnsolve. It will be resized, typically it will be |
1471 | | * larger. |
1472 | | * \param values The eigenvalue matrix, as returned by \ref |
1473 | | * igraph_arpack_rnsolve. It will be resized, typically extra, |
1474 | | * unneeded rows (=eigenvalues) will be removed. |
1475 | | * \param nev The number of eigenvalues/vectors to keep. Can be less |
1476 | | * or equal than the number originally requested from ARPACK. |
1477 | | * \return Error code. |
1478 | | * |
1479 | | * Time complexity: linear in the number of elements in the \p vectors |
1480 | | * matrix. |
1481 | | */ |
1482 | | |
1483 | | igraph_error_t igraph_arpack_unpack_complex(igraph_matrix_t *vectors, igraph_matrix_t *values, |
1484 | 0 | igraph_integer_t nev) { |
1485 | |
|
1486 | 0 | igraph_integer_t nodes = igraph_matrix_nrow(vectors); |
1487 | 0 | igraph_integer_t no_evs = igraph_matrix_nrow(values); |
1488 | 0 | igraph_integer_t i, j; |
1489 | 0 | igraph_integer_t new_vector_pos, vector_pos; |
1490 | 0 | igraph_matrix_t new_vectors; |
1491 | | |
1492 | | /* Error checks */ |
1493 | 0 | if (nev < 0) { |
1494 | 0 | IGRAPH_ERROR("`nev' cannot be negative.", IGRAPH_EINVAL); |
1495 | 0 | } |
1496 | 0 | if (nev > no_evs) { |
1497 | 0 | IGRAPH_ERROR("`nev' too large, we don't have that many in `values'.", |
1498 | 0 | IGRAPH_EINVAL); |
1499 | 0 | } |
1500 | | |
1501 | 0 | for (i = no_evs -1; i >= nev; i--) { |
1502 | 0 | IGRAPH_CHECK(igraph_matrix_remove_row(values, i)); |
1503 | 0 | } |
1504 | | |
1505 | 0 | IGRAPH_CHECK(igraph_matrix_init(&new_vectors, nodes, nev * 2)); |
1506 | 0 | IGRAPH_FINALLY(igraph_matrix_destroy, &new_vectors); |
1507 | |
|
1508 | 0 | new_vector_pos = 0; |
1509 | 0 | vector_pos = 0; |
1510 | 0 | for (i = 0; i < nev && vector_pos < igraph_matrix_ncol(vectors); i++) { |
1511 | 0 | if (MATRIX(*values, i, 1) == 0) { |
1512 | | /* Real eigenvalue */ |
1513 | 0 | for (j = 0; j < nodes; j++) { |
1514 | 0 | MATRIX(new_vectors, j, new_vector_pos) = MATRIX(*vectors, j, vector_pos); |
1515 | 0 | } |
1516 | 0 | new_vector_pos += 2; |
1517 | 0 | vector_pos += 1; |
1518 | 0 | } else { |
1519 | | /* complex eigenvalue */ |
1520 | 0 | for (j = 0; j < nodes; j++) { |
1521 | 0 | MATRIX(new_vectors, j, new_vector_pos) = MATRIX(*vectors, j, vector_pos); |
1522 | 0 | MATRIX(new_vectors, j, new_vector_pos + 1) = MATRIX(*vectors, j, vector_pos + 1); |
1523 | 0 | } |
1524 | | |
1525 | | /* handle the conjugate */ |
1526 | | |
1527 | | /* first check if the conjugate eigenvalue is there */ |
1528 | 0 | i++; |
1529 | 0 | if (i >= nev) { |
1530 | 0 | break; |
1531 | 0 | } |
1532 | | |
1533 | 0 | if (MATRIX(*values, i, 1) != -MATRIX(*values, i-1, 1)) { |
1534 | 0 | IGRAPH_ERROR("Complex eigenvalue not followed by its conjugate.", IGRAPH_EINVAL); |
1535 | 0 | } |
1536 | | |
1537 | | /* then copy and negate */ |
1538 | 0 | for (j = 0; j < nodes; j++) { |
1539 | 0 | MATRIX(new_vectors, j, new_vector_pos + 2) = MATRIX(*vectors, j, vector_pos); |
1540 | 0 | MATRIX(new_vectors, j, new_vector_pos + 3) = -MATRIX(*vectors, j, vector_pos + 1); |
1541 | 0 | } |
1542 | 0 | new_vector_pos += 4; |
1543 | 0 | vector_pos += 2; |
1544 | 0 | } |
1545 | 0 | } |
1546 | 0 | igraph_matrix_destroy(vectors); |
1547 | 0 | IGRAPH_CHECK(igraph_matrix_init_copy(vectors, &new_vectors)); |
1548 | 0 | igraph_matrix_destroy(&new_vectors); |
1549 | 0 | IGRAPH_FINALLY_CLEAN(1); |
1550 | |
|
1551 | 0 | return IGRAPH_SUCCESS; |
1552 | 0 | } |