244 lines
7.3 KiB
C
244 lines
7.3 KiB
C
/*
|
|
igraph library.
|
|
Copyright (C) 2011-2012 Gabor Csardi <csardi.gabor@gmail.com>
|
|
334 Harvard street, Cambridge MA, 02139 USA
|
|
|
|
This program is free software; you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation; either version 2 of the License, or
|
|
(at your option) any later version.
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with this program; if not, write to the Free Software
|
|
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
|
02110-1301 USA
|
|
|
|
*/
|
|
|
|
#include <igraph.h>
|
|
#include <pthread.h>
|
|
#include <string.h>
|
|
|
|
#include "linalg/arpack_internal.h"
|
|
|
|
#include "test_utilities.h"
|
|
|
|
/* Test whether ARPACK is thread-safe. We will create two threads,
|
|
each calling a different ARPACK eigensolver. We will make sure that
|
|
the ARPACK calls from the two threads overlap */
|
|
|
|
typedef struct thread_data_t {
|
|
igraph_matrix_t *m;
|
|
igraph_vector_t *result;
|
|
pthread_cond_t *cond;
|
|
pthread_mutex_t *mutex;
|
|
int *steps, *othersteps;
|
|
} thread_data_t;
|
|
|
|
int arpack_mult(igraph_real_t *to, igraph_real_t *from,
|
|
igraph_matrix_t *matrix) {
|
|
/* TODO */
|
|
igraph_blas_dgemv_array(/*transpose=*/ 0, /*alpha=*/ 1.0, matrix,
|
|
from, /*beta=*/ 0.0, to);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* This is the function performed by each thread. It calls the
|
|
low-level ARPACK symmetric eigensolver, step by step. After each
|
|
step, it synchronizes with the other thread.
|
|
|
|
The synchronization ensures that the two threads are using the
|
|
thread-local variables at the same time. If they are really
|
|
thread-local, then ARPACK still delivers the correct solution for
|
|
the two matrices. Otherwise the result is undefined: maybe results
|
|
will be incorrect, or the program will crash.
|
|
|
|
This function is basically a simplified copy of igraph_arpack_rssolve.
|
|
*/
|
|
|
|
void *thread_function(void *arg) {
|
|
thread_data_t *data = (thread_data_t*) arg;
|
|
igraph_matrix_t *M = data->m;
|
|
igraph_vector_t *result = data->result;
|
|
pthread_cond_t *cond = data->cond;
|
|
pthread_mutex_t *mutex = data->mutex;
|
|
igraph_arpack_options_t options;
|
|
igraph_real_t *v, *workl, *workd, *d, *resid, *ax;
|
|
int *select;
|
|
int ido = 0;
|
|
#if IGRAPH_THREAD_SAFE
|
|
int rvec = 1;
|
|
char *all = "All";
|
|
#endif
|
|
int i;
|
|
|
|
igraph_arpack_options_init(&options);
|
|
options.n = igraph_matrix_nrow(M);
|
|
options.ldv = options.n;
|
|
options.nev = 1;
|
|
options.ncv = 3;
|
|
options.lworkl = options.ncv * (options.ncv + 8);
|
|
options.which[0] = 'L';
|
|
options.which[1] = 'M';
|
|
|
|
options.iparam[0] = options.ishift;
|
|
options.iparam[2] = options.mxiter;
|
|
options.iparam[3] = options.nb;
|
|
options.iparam[4] = 0;
|
|
options.iparam[6] = options.mode;
|
|
options.info = options.start;
|
|
|
|
v = IGRAPH_CALLOC(options.ldv * options.ncv, igraph_real_t);
|
|
workl = IGRAPH_CALLOC(options.lworkl, igraph_real_t);
|
|
workd = IGRAPH_CALLOC(3 * options.n, igraph_real_t);
|
|
d = IGRAPH_CALLOC(2 * options.ncv, igraph_real_t);
|
|
resid = IGRAPH_CALLOC(options.n, igraph_real_t);
|
|
ax = IGRAPH_CALLOC(options.n, igraph_real_t);
|
|
select = IGRAPH_CALLOC(options.ncv, int);
|
|
|
|
if (!v || !workl || !workd || !d || !resid || !ax || !select) {
|
|
printf("Out of memory\n");
|
|
return 0;
|
|
}
|
|
|
|
while (1) {
|
|
#if IGRAPH_THREAD_SAFE
|
|
igraphdsaupd_(&ido, options.bmat, &options.n, options.which,
|
|
&options.nev, &options.tol, resid, &options.ncv, v,
|
|
&options.ldv, options.iparam, options.ipntr, workd,
|
|
workl, &options.lworkl, &options.info);
|
|
#endif
|
|
|
|
if (ido == -1 || ido == 1) {
|
|
|
|
igraph_real_t *from = workd + options.ipntr[0] - 1;
|
|
igraph_real_t *to = workd + options.ipntr[1] - 1;
|
|
arpack_mult(to, from, M);
|
|
|
|
} else {
|
|
break;
|
|
}
|
|
|
|
pthread_mutex_lock(mutex);
|
|
*(data->steps) += 1;
|
|
if ( *(data->othersteps) == *(data->steps) ) {
|
|
pthread_cond_signal(cond);
|
|
}
|
|
|
|
while ( *(data->othersteps) < * (data->steps) && *(data->othersteps) != -1 ) {
|
|
pthread_cond_wait(cond, mutex);
|
|
}
|
|
pthread_mutex_unlock(mutex);
|
|
}
|
|
|
|
pthread_mutex_lock(mutex);
|
|
*data->steps = -1;
|
|
pthread_cond_signal(cond);
|
|
pthread_mutex_unlock(mutex);
|
|
|
|
if (options.info != 0) {
|
|
printf("ARPACK error\n");
|
|
return 0;
|
|
}
|
|
|
|
#if IGRAPH_THREAD_SAFE
|
|
igraphdseupd_(&rvec, all, select, d, v, &options.ldv,
|
|
&options.sigma, options.bmat, &options.n,
|
|
options.which, &options.nev, &options.tol,
|
|
resid, &options.ncv, v, &options.ldv, options.iparam,
|
|
options.ipntr, workd, workl, &options.lworkl,
|
|
&options.ierr);
|
|
#endif
|
|
|
|
if (options.ierr != 0) {
|
|
printf("ARPACK error\n");
|
|
return 0;
|
|
}
|
|
|
|
igraph_vector_resize(result, options.n);
|
|
for (i = 0; i < options.n; i++) {
|
|
VECTOR(*result)[i] = v[i];
|
|
}
|
|
|
|
if (VECTOR(*result)[0] < 0) {
|
|
igraph_vector_scale(result, -1.0);
|
|
}
|
|
|
|
free(v);
|
|
free(workl);
|
|
free(workd);
|
|
free(d);
|
|
free(resid);
|
|
free(ax);
|
|
free(select);
|
|
|
|
return 0;
|
|
}
|
|
|
|
int main(void) {
|
|
pthread_t thread_id1, thread_id2;
|
|
void *exit_status1, *exit_status2;
|
|
igraph_matrix_t m1, m2;
|
|
igraph_vector_t result1, result2;
|
|
pthread_cond_t steps_cond = PTHREAD_COND_INITIALIZER;
|
|
pthread_mutex_t steps_mutex = PTHREAD_MUTEX_INITIALIZER;
|
|
int steps1 = 0, steps2 = 0;
|
|
thread_data_t
|
|
data1 = { &m1, &result1, &steps_cond, &steps_mutex, &steps1, &steps2 },
|
|
data2 = { &m2, &result2, &steps_cond, &steps_mutex, &steps2, &steps1 };
|
|
int i, j;
|
|
|
|
/* Skip if igraph is not thread safe */
|
|
if (!IGRAPH_THREAD_SAFE) {
|
|
return 77;
|
|
}
|
|
|
|
igraph_matrix_init(&m1, 10, 10);
|
|
igraph_matrix_init(&m2, 10, 10);
|
|
igraph_vector_init(&result1, igraph_matrix_nrow(&m1));
|
|
igraph_vector_init(&result2, igraph_matrix_nrow(&m2));
|
|
|
|
igraph_rng_seed(igraph_rng_default(), 42);
|
|
|
|
for (i = 0; i < igraph_matrix_nrow(&m1); i++) {
|
|
for (j = 0; j <= i; j++) {
|
|
MATRIX(m1, i, j) = MATRIX(m1, j, i) =
|
|
igraph_rng_get_integer(igraph_rng_default(), 0, 10);
|
|
}
|
|
}
|
|
|
|
for (i = 0; i < igraph_matrix_nrow(&m2); i++) {
|
|
for (j = 0; j <= i; j++) {
|
|
MATRIX(m2, i, j) = MATRIX(m2, j, i) =
|
|
igraph_rng_get_integer(igraph_rng_default(), 0, 10);
|
|
}
|
|
}
|
|
|
|
pthread_create(&thread_id1, NULL, thread_function, (void *) &data1);
|
|
pthread_create(&thread_id2, NULL, thread_function, (void *) &data2);
|
|
|
|
pthread_join(thread_id1, &exit_status1);
|
|
pthread_join(thread_id2, &exit_status2);
|
|
|
|
igraph_matrix_print(&m1);
|
|
igraph_vector_print(&result1);
|
|
printf("---\n");
|
|
igraph_matrix_print(&m2);
|
|
igraph_vector_print(&result2);
|
|
|
|
igraph_vector_destroy(&result1);
|
|
igraph_vector_destroy(&result2);
|
|
igraph_matrix_destroy(&m1);
|
|
igraph_matrix_destroy(&m2);
|
|
|
|
VERIFY_FINALLY_STACK();
|
|
|
|
return 0;
|
|
}
|