/*! \brief Computes the graph corresponding to the precision matrix <em>\b Q</em> based on the weight function. This function converts a graph definition on the weights \f$ w_{ij} \f$ of a weighted average GMRF model, as defined by <b>(GMRF-8)</b> in \ref specification, to the graph defining the precision matrix <em>\b Q</em> of the GMRF <em>\b x</em>. \param[out] wa_problem At output, \a wa_problem is allocated as a pointer to a \c GMRFLib_wa_problem_tp, holding the graph of the GMRF <em>\b x</em>, the function defining the <em>\b Q</em> -matrix and a \em character -pointer holding the address of the variable or data structure holding it's arguments. \param[in] wagraph The graph of the weights \f$ {w_{ij}} \f$ in the density function of <em>\b x</em>, of the form <b>(GMRF-8)</b>. \param[in] wafunc A pointer to a function returning the weights \f$ w_{ij} \f$. The function is to be of the same format as the function defining the elements of the <em>\b Q</em> -matrix of a general GMRF, such that the argument list and return value should be the same as for the template \c GMRFLib_Qfunc_tp(). The arguments should be the indices \em i and \em j and a \em character -pointer referring to additional arguments to the function. If \f$ w_{ij}=0 \f$ initially, it is required to be kept unchanged. \param[in] wafunc_arg A \em character -pointer holding the address of a variable or data structure defining additional arguments to the function \a wafunc. \remarks Based on the weight function, defining the weights in <b>(GMRF-8)</b>, the routine computes the graph corresponding to the precision matrix <em>\b Q</em> of the GMRF \em x. Also, the function defining the elements of <em>\b Q</em> and the set of arguments (in addition to the indices \em i and \em j) are generated. These are returned as members of the \c GMRFLib_wa_problem_tp -object <em>(*problem)</em>. \note The weights \f$ w_{ij} \f$ that are initially defined to be 0, should be kept fixed. The function \c GMRFLib_prune_graph() is called on the resulting graph of the GMRF <em>\b x</em>, removing elements of the graph corresponding to <em>\b Q (i,j) = 0</em>. Re-setting the zero weights, for example by letting the weights depend on parameters that might change while running the programs, will invalidate this graph reduction.\n This routine will in most cases compute <em>\b Q (i,j)</em> less efficiently using more memory than a tailored implementation, but may save you for a lot of work!!! \n There is a global variable \c GMRFLib_use_wa_table_lookup() which controls the internal behaviour: if it is \c #GMRFLib_TRUE (default value) then internal lookup-tables are build that (can really) speed up the computation, and if it is \c #GMRFLib_FALSE, then it does not use internal lookup-tables. The storage requirement is \f$ {\mathcal O}(n^{3/2}) \f$. \par Example: See \ref ex_wa \sa GMRFLib_free_wa_problem, GMRFLib_prune_graph. */ int GMRFLib_init_wa_problem(GMRFLib_wa_problem_tp **wa_problem, GMRFLib_graph_tp *wagraph, GMRFLib_Qfunc_tp *wafunc, char *wafunc_arg) { /* wagraph is the graph defining (*) \sum_i (w_ii x_i - \sum_{j~i} w_ij x_j)^2 = x^T Q x where 'wafunc(i,j)' returns w_ij with arguments wafunc_arg. this routine compute the corresponding graph defining Q, a function returning Q_ij and its arguments. */ int i, j, k, kk, jj, jjj, kkk, n, *memsiz = NULL, nnb, *hold = NULL, indx; GMRFLib_graph_tp *graph = NULL, *ngraph = NULL; GMRFLib_waQfunc_arg_tp *wa_arg = NULL; GMRFLib_make_empty_graph(&graph); n = wagraph->n; graph->n = n; graph->nnbs = Calloc(n, int); MEMCHK(graph->nnbs); graph->nbs = Calloc(n, int *); MEMCHK(graph->nbs); memsiz = Calloc(n, int); MEMCHK(memsiz); for(i=0;i<n;i++) { graph->nnbs[i] = wagraph->nnbs[i]; memsiz[i] = MAX(1, 2*graph->nnbs[i]); graph->nbs[i] = Calloc(memsiz[i], int); if (graph->nnbs[i]) memcpy(graph->nbs[i], wagraph->nbs[i], graph->nnbs[i]*sizeof(int)); } /* build the new graph */ if (0) { /* old code, slow algorithm..... */ for(i=0;i<n;i++) for(j=0;j<wagraph->nnbs[i];j++) for(jj=j+1;jj<wagraph->nnbs[i];jj++) { k = wagraph->nbs[i][j]; kk = wagraph->nbs[i][jj]; if (!GMRFLib_is_neighb(k, kk, graph)) { /* add, must do it symmetrically! must also sort the neighbours, since the 'is_neig' function assume they are sorted. */ graph->nnbs[k]++; if (graph->nnbs[k] > memsiz[k]) { memsiz[k] *= 2; graph->nbs[k] = (int *)realloc(graph->nbs[k], memsiz[k]*sizeof(int)); } graph->nbs[k][graph->nnbs[k]-1] = kk; qsort(graph->nbs[k], (size_t)graph->nnbs[k], sizeof(int), GMRFLib_intcmp); graph->nnbs[kk]++; if (graph->nnbs[kk] > memsiz[kk]) { memsiz[kk] *= 2; graph->nbs[kk] = (int *)realloc(graph->nbs[kk], memsiz[kk]*sizeof(int)); } graph->nbs[kk][graph->nnbs[kk]-1] = k; qsort(graph->nbs[kk], (size_t)graph->nnbs[kk], sizeof(int), GMRFLib_intcmp); } } } else { /* new version, runs faster, use slightly more memory, but not that much */ if (0) printf("\n\n%s:%1d:NEW CODE HERE, NOT PROPERLY TESTED!!!\n\n", __FILE__, __LINE__); for(i=0;i<n;i++) for(j=0;j<wagraph->nnbs[i];j++) { k = wagraph->nbs[i][j]; for(jj=j+1;jj<wagraph->nnbs[i];jj++) { kk = wagraph->nbs[i][jj]; if (graph->nnbs[k] >= memsiz[k]) { qsort(graph->nbs[k], (size_t)graph->nnbs[k], sizeof(int), GMRFLib_intcmp); for(jjj=1, kkk=0; jjj<graph->nnbs[k];jjj++) if (graph->nbs[k][jjj] != graph->nbs[k][kkk]) graph->nbs[k][++kkk] = graph->nbs[k][jjj]; graph->nnbs[k] = kkk+1; } if (graph->nnbs[k] >= memsiz[k]) { memsiz[k] *= 2; graph->nbs[k] = (int *)realloc(graph->nbs[k], memsiz[k]*sizeof(int)); } graph->nbs[k][graph->nnbs[k]++] = kk; if (graph->nnbs[kk] >= memsiz[kk]) { qsort(graph->nbs[kk], (size_t)graph->nnbs[kk], sizeof(int), GMRFLib_intcmp); for(jjj=1, kkk=0; jjj<graph->nnbs[kk];jjj++) if (graph->nbs[kk][jjj] != graph->nbs[kk][kkk]) graph->nbs[kk][++kkk] = graph->nbs[kk][jjj]; graph->nnbs[kk] = kkk+1; } if (graph->nnbs[kk] >= memsiz[kk]) { memsiz[kk] *= 2; graph->nbs[kk] = (int *)realloc(graph->nbs[kk], memsiz[kk]*sizeof(int)); } graph->nbs[kk][graph->nnbs[kk]++] = k; } } for(i=0;i<n;i++) /* this step is needed */ if (graph->nnbs[i]) { qsort(graph->nbs[i], (size_t)graph->nnbs[i], sizeof(int), GMRFLib_intcmp); for(j=1, k=0;j<graph->nnbs[i];j++) if (graph->nbs[i][j] != graph->nbs[i][k]) graph->nbs[i][++k] = graph->nbs[i][j]; graph->nnbs[i] = k+1; } } for(i=0, nnb=0;i<n;i++) nnb += graph->nnbs[i]; if (nnb) { hold = Calloc(nnb, int); MEMCHK(hold); /* use a linear storage */ } for(i=0, indx=0;i<n;i++) { if (graph->nnbs[i]) { memcpy(&hold[indx], graph->nbs[i], graph->nnbs[i]*sizeof(int)); FREE(graph->nbs[i]); graph->nbs[i] = &hold[indx]; } else FREE(graph->nbs[i]); indx += graph->nnbs[i]; } FREE(memsiz); GMRFLib_prepare_graph(graph); /* setup the new types */ wa_arg = Calloc(1, GMRFLib_waQfunc_arg_tp); MEMCHK(wa_arg); wa_arg->waQgraph = graph; wa_arg->waQfunc = wafunc; wa_arg->waQfunc_arg = wafunc_arg; GMRFLib_copy_graph(&(wa_arg->wagraph), wagraph); /* yes, make a copy! */ if (GMRFLib_use_wa_table_lookup) { /* build hash table for idx = (i,j) -> neigb_info[idx] */ double idx=0.0; spmatrix_init_hint(&(wa_arg->neigb_idx_hash), (mapkit_size_t)GMRFLib_nQelm(graph)); /* give a hint of the size */ wa_arg->neigb_idx_hash.alwaysdefault = 0; for(i=0;i<graph->n;i++) { spmatrix_set(&(wa_arg->neigb_idx_hash), i, i, idx); idx++; for(j=0;j<graph->nnbs[i];j++) { spmatrix_set(&(wa_arg->neigb_idx_hash), i, graph->nbs[i][j], idx); idx++; } } wa_arg->n_neigb_info = (int) idx; /* hold list of neighbors and common neigbhbors, to gain some real speedup.... */ wa_arg->neigb_info = Calloc(wa_arg->n_neigb_info, GMRFLib_node_list_tp *); MEMCHK(wa_arg->neigb_info); } else
/*! \brief Computes the graph corresponding to the precision matrix <em>\b Q</em> based on the weight function. This function converts a graph definition on the weights \f$ w_{ij} \f$ of a weighted average GMRF model, as defined by <b>(GMRF-8)</b> in \ref specification, to the graph defining the precision matrix <em>\b Q</em> of the GMRF <em>\b x</em>. \param[out] wa_problem At output, \a wa_problem is allocated as a pointer to a \c GMRFLib_wa_problem_tp, holding the graph of the GMRF <em>\b x</em>, the function defining the <em>\b Q</em> -matrix and a \em void -pointer holding the address of the variable or data structure holding it's arguments. \param[in] wagraph The graph of the weights \f$ {w_{ij}} \f$ in the density function of <em>\b x</em>, of the form <b>(GMRF-8)</b>. \param[in] wafunc A pointer to a function returning the weights \f$ w_{ij} \f$. The function is to be of the same format as the function defining the elements of the <em>\b Q</em> -matrix of a general GMRF, such that the argument list and return value should be the same as for the template \c GMRFLib_Qfunc_tp(). The arguments should be the indices \em i and \em j and a \em void -pointer referring to additional arguments to the function. If \f$ w_{ij}=0 \f$ initially, it is required to be kept unchanged. \param[in] wafunc_arg A \em void -pointer holding the address of a variable or data structure defining additional arguments to the function \a wafunc. \remarks Based on the weight function, defining the weights in <b>(GMRF-8)</b>, the routine computes the graph corresponding to the precision matrix <em>\b Q</em> of the GMRF \em x. Also, the function defining the elements of <em>\b Q</em> and the set of arguments (in addition to the indices \em i and \em j) are generated. These are returned as members of the \c GMRFLib_wa_problem_tp -object <em>(*problem)</em>. \note The weights \f$ w_{ij} \f$ that are initially defined to be 0, should be kept fixed. The function \c GMRFLib_prune_graph() is called on the resulting graph of the GMRF <em>\b x</em>, removing elements of the graph corresponding to <em>\b Q (i,j) = 0</em>. Re-setting the zero weights, for example by letting the weights depend on parameters that might change while running the programs, will invalidate this graph reduction.\n This routine will in most cases compute <em>\b Q (i,j)</em> less efficiently using more memory than a tailored implementation, but may save you for a lot of work!!! \n There is a global variable \c GMRFLib_use_wa_table_lookup() which controls the internal behaviour: if it is \c #GMRFLib_TRUE (default value) then internal lookup-tables are build that (can really) speed up the computation, and if it is \c #GMRFLib_FALSE, then it does not use internal lookup-tables. The storage requirement is \f$ {\mathcal O}(n^{3/2}) \f$. \par Example: See \ref ex_wa \sa GMRFLib_free_wa_problem, GMRFLib_prune_graph. */ int GMRFLib_init_wa_problem(GMRFLib_wa_problem_tp ** wa_problem, GMRFLib_graph_tp * wagraph, GMRFLib_Qfunc_tp * wafunc, void *wafunc_arg) { /* * wagraph is the graph defining * * (*) \sum_i (w_ii x_i - \sum_{j~i} w_ij x_j)^2 = x^T Q x * * where 'wafunc(i,j)' returns w_ij with arguments wafunc_arg. this routine compute the corresponding graph defining Q, * a function returning Q_ij and its arguments. */ int i, j, k, kk, jj, jjj, kkk, n, *memsiz = NULL, nnb, *hold = NULL, indx; GMRFLib_graph_tp *graph = NULL, *ngraph = NULL; GMRFLib_waQfunc_arg_tp *wa_arg = NULL; GMRFLib_make_empty_graph(&graph); n = wagraph->n; graph->n = n; graph->nnbs = Calloc(n, int); graph->nbs = Calloc(n, int *); memsiz = Calloc(n, int); for (i = 0; i < n; i++) { graph->nnbs[i] = wagraph->nnbs[i]; memsiz[i] = IMAX(1, 2 * graph->nnbs[i]); graph->nbs[i] = Calloc(memsiz[i], int); if (graph->nnbs[i]) memcpy(graph->nbs[i], wagraph->nbs[i], graph->nnbs[i] * sizeof(int)); } /* * build the new graph */ if (0) { /* * old code, slow algorithm..... */ for (i = 0; i < n; i++) for (j = 0; j < wagraph->nnbs[i]; j++) for (jj = j + 1; jj < wagraph->nnbs[i]; jj++) { k = wagraph->nbs[i][j]; kk = wagraph->nbs[i][jj]; if (!GMRFLib_is_neighb(k, kk, graph)) { /* * add, must do it symmetrically! must also sort the neighbours, since the * 'is_neig' function assume they are sorted. */ graph->nnbs[k]++; if (graph->nnbs[k] > memsiz[k]) { memsiz[k] *= 2; graph->nbs[k] = Realloc(graph->nbs[k], memsiz[k], int); } graph->nbs[k][graph->nnbs[k] - 1] = kk; qsort(graph->nbs[k], (size_t) graph->nnbs[k], sizeof(int), GMRFLib_icmp); graph->nnbs[kk]++; if (graph->nnbs[kk] > memsiz[kk]) { memsiz[kk] *= 2; graph->nbs[kk] = Realloc(graph->nbs[kk], memsiz[kk], int); } graph->nbs[kk][graph->nnbs[kk] - 1] = k; qsort(graph->nbs[kk], (size_t) graph->nnbs[kk], sizeof(int), GMRFLib_icmp); }
/*! \brief Reads a graph from a file, binary format \param[in,out] graph At output, <em>(*graph)</em> has been initialized by using the information on the file \em filename. \param[in] filename The name of the file, binary formatted similar to \c GMRFLib_read_graph() (without newlines), containing the specification of the graph. \sa GMRFLib_read_graph, GMRFLib_write_graph_binary */ int GMRFLib_read_graph_binary_EXPERIMENTAL(GMRFLib_graph_tp ** graph, const char *filename) { /* * read a graph in the following format * * N node[0] nnbs[0] nbs[node[0]][0] nbs[node[0]][1] ... nbs[node[0]][nnbs[0]-1] node[1] nnbs[1] nbs[node[1]][0] * nbs[node[1]][1] ... nbs[node[1]][nnbs[1]-1] : node[N-1] nnbs[N-1] nbs[node[N-1]][0] nbs[node[N-1]][1] ... * nbs[node[N-1]][nnbs[N-1]-1] */ #define READ_ERROR() do { \ if (1){\ fprintf(stderr,"\n\n\t%s: error: file [%s]:\n",__GMRFLib_FuncName,filename);\ fprintf(stderr,"\t\tfail to read [%1lu] bytes from byte [%1lu]\n", (unsigned long)(nelm*sizeof(int)), (unsigned long)byte); \ } \ if (fp) fclose(fp);\ (*graph) = NULL;\ GMRFLib_ERROR(GMRFLib_EREADFILE);} while(0) int *storage, n_neig_tot = 0, storage_indx; int i, byte, tnode; size_t nelm; FILE *fp; if (!filename) { return GMRFLib_SUCCESS; } if (!(fp = fopen(filename, "r"))) { GMRFLib_ERROR(GMRFLib_EOPENFILE); } GMRFLib_make_empty_graph(graph); byte = 0; nelm = 1; if (fread(&((*graph)->n), sizeof(int), nelm, fp) != 1) { READ_ERROR(); } byte += sizeof(int); (*graph)->nnbs = Calloc((*graph)->n, int); (*graph)->nbs = Calloc((*graph)->n, int *); for (i = 0; i < (*graph)->n; i++) { nelm = 1; if (fread(&tnode, sizeof(int), nelm, fp) != 1) { READ_ERROR(); /* target node */ } byte += sizeof(int); if (tnode < 0 || tnode >= (*graph)->n) { fprintf(stderr, "\n\n\t%s: error: file [%s]: byte[%1d]\n", __GMRFLib_FuncName, filename, byte); fprintf(stderr, "\t\tnode-number[%1d] is not in the range[0:%1d]\n", tnode, (*graph)->n); if (fp) { fclose(fp); } GMRFLib_ERROR(GMRFLib_EPARAMETER); } nelm = 1; if (fread(&((*graph)->nnbs[tnode]), sizeof(int), nelm, fp) != 1) { READ_ERROR(); } byte += sizeof(int); n_neig_tot += (*graph)->nnbs[tnode]; if ((*graph)->nnbs[tnode]) { (*graph)->nbs[tnode] = Calloc((*graph)->nnbs[tnode], int); nelm = (*graph)->nnbs[tnode]; if (fread((*graph)->nbs[tnode], sizeof(int), nelm, fp) != nelm) { READ_ERROR(); } byte += sizeof(int) * ((*graph)->nnbs[tnode]); } else {
/** \brief Build a graph of type GMRFLib_graph_tp from the editable graph-object \param[out] graph The graph of type GMRFLib_graph_tp is returned as \a *graph \param[in] ged The editable graph-object \sa GMRFLib_graph_tp */ int GMRFLib_ged_build(GMRFLib_graph_tp ** graph, GMRFLib_ged_tp * ged) { #define NOMAP (-1) /* * build the graph */ GMRFLib_graph_tp *g; int i, j, jj, n, *nnbs, node = -1, nnode = -1, **nbs, *map, *imap, n_new; unsigned char *node_in_use, *indep; map_ii **hash; spmatrix_storage *sptr; map_ii_storage *iptr; /* * number of (possible) nodes */ n = ged->max_node + 1; /* * flag nodes in use */ node_in_use = Calloc(n, unsigned char); for (sptr = NULL; (sptr = spmatrix_nextptr(&(ged->Q), sptr)) != NULL;) { if (sptr->value != 0.0 && sptr->key.key1 == sptr->key.key2) { node_in_use[sptr->key.key1] = 1; } } /* * add global nodes (if any) */ for (j = 0, iptr = NULL; (iptr = map_ii_nextptr(&(ged->tags), iptr)) != NULL;) { if (iptr->value == GMRFLib_GED_TAG_GLOBAL) { i = iptr->key; if (node_in_use[i]) { for (jj = 0; jj < n; jj++) { if (node_in_use[jj] && jj != i) { GMRFLib_ged_add(ged, i, jj); } } } } } /* * flag indep nodes (if any) */ indep = Calloc(n, unsigned char); for (j = 0, iptr = NULL; (iptr = map_ii_nextptr(&(ged->tags), iptr)) != NULL;) { if (iptr->value == GMRFLib_GED_TAG_INDEP) { indep[iptr->key] = 1; } } imap = Calloc(n, int); map = Calloc(n, int); nbs = Calloc(n, int *); nnbs = Calloc(n, int); hash = Calloc(n, map_ii *); for (i = 0; i < n; i++) { hash[i] = Calloc(1, map_ii); map_ii_init(hash[i]); } /* * find the mapping */ for (i = 0; i < n; i++) { map[i] = imap[i] = NOMAP; } for (i = j = n_new = 0; i < n; i++) { if (node_in_use[i]) { map[i] = j; imap[j] = i; j++; n_new++; } } /* * find the neighbours */ for (sptr = NULL; (sptr = spmatrix_nextptr(&(ged->Q), sptr)) != NULL;) { if (sptr->value != 0.0 && sptr->key.key1 != sptr->key.key2) { node = IMIN(sptr->key.key1, sptr->key.key2); nnode = IMAX(sptr->key.key1, sptr->key.key2); assert(LEGAL(node, n) && LEGAL(nnode, n)); if (node_in_use[node] && node_in_use[nnode] && !indep[node] && !indep[nnode]) { map_ii_set(hash[node], nnode, 1); map_ii_set(hash[nnode], node, 1); nnbs[node]++; nnbs[nnode]++; } } } /* * make the nbs-array */ for (i = 0; i < n; i++) { if (nnbs[i]) { nbs[i] = Calloc(nnbs[i], int); for (j = 0, iptr = NULL; (iptr = map_ii_nextptr(hash[i], iptr)) != NULL;) { nbs[i][j++] = iptr->key; } } } /* * make the graph-object. copy it into a new one to make the correct memory-layout */ for (i = 0; i < n_new; i++) { nnbs[i] = nnbs[imap[i]]; /* ok, as imap[i] >= i */ nbs[i] = nbs[imap[i]]; /* ok, as imap[i] >= i */ } for (i = 0; i < n_new; i++) { for (jj = 0; jj < nnbs[i]; jj++) { nbs[i][jj] = map[nbs[i][jj]]; /* map to the new nodes */ } } GMRFLib_make_empty_graph(&g); g->n = n_new; g->nnbs = nnbs; g->nbs = nbs; g->mothergraph_idx = imap; /* preserve the mapping */ GMRFLib_copy_graph(graph, g); GMRFLib_prepare_graph(*graph); if (0) { /* * validate the graph? this should not be needed, as this function will ensurethe graph should be always be consistent. * In any case, enable this for the moment until these tools are sufficiently validated. */ GMRFLib_EWRAP0(GMRFLib_validate_graph(stderr, *graph)); } /* * cleanup */ for (i = 0; i < n_new; i++) { Free(nbs[i]); } Free(nbs); Free(nnbs); Free(node_in_use); Free(map); Free(imap); Free(g); Free(indep); for (i = 0; i < n; i++) { map_ii_free(hash[i]); Free(hash[i]); } Free(hash); return GMRFLib_SUCCESS; #undef NOMAP }