/* * graph_test() * * Tests graph g to be valid. Checks that g is non-NULL, the edges are * symmetric and anti-reflexive, and that all vertex weights are positive. * If output is non-NULL, prints a few lines telling the status of the graph * to file descriptor output. * * Returns TRUE if the graph is valid, FALSE otherwise. */ boolean graph_test(graph_t *g,FILE *output) { int i,j; int edges=0; int asymm=0; int nonpos=0; int refl=0; int extra=0; unsigned int weight=0; boolean weighted; ASSERT((sizeof(setelement)*8)==ELEMENTSIZE); if (g==NULL) { if (output) fprintf(output," WARNING: Graph pointer is NULL!\n"); return FALSE; } weighted=graph_weighted(g); for (i=0; i < g->n; i++) { if (g->edges[i]==NULL) { if (output) fprintf(output," WARNING: Graph edge set " "NULL!\n" " (further warning suppressed)\n"); return FALSE; } if (SET_MAX_SIZE(g->edges[i]) < g->n) { if (output) fprintf(output," WARNING: Graph edge set " "too small!\n" " (further warnings suppressed)\n"); return FALSE; } for (j=0; j < g->n; j++) { if (SET_CONTAINS_FAST(g->edges[i],j)) { edges++; if (i==j) { refl++; } if (!SET_CONTAINS_FAST(g->edges[j],i)) { asymm++; } } } for (j=g->n; j < SET_ARRAY_LENGTH(g->edges[i])*ELEMENTSIZE; j++) { if (SET_CONTAINS_FAST(g->edges[i],j)) extra++; } if (g->weights[i] <= 0) nonpos++; if (weight<INT_MAX) weight += g->weights[i]; } edges/=2; /* Each is counted twice. */ if (output) { /* Semi-weighted means all weights are equal, but not 1. */ fprintf(output,"%s graph has %d vertices, %d edges " "(density %.2f).\n", weighted?"Weighted": ((g->weights[0]==1)?"Unweighted":"Semi-weighted"), g->n,edges,(float)edges/((float)(g->n - 1)*(g->n)/2)); if (asymm) fprintf(output," WARNING: Graph contained %d " "asymmetric edges!\n",asymm); if (refl) fprintf(output," WARNING: Graph contained %d " "reflexive edges!\n",refl); if (nonpos) fprintf(output," WARNING: Graph contained %d " "non-positive vertex weights!\n",nonpos); if (extra) fprintf(output," WARNING: Graph contained %d edges " "to non-existent vertices!\n",extra); if (weight>=INT_MAX) fprintf(output," WARNING: Total graph weight >= " "INT_MAX!\n"); if (asymm==0 && refl==0 && nonpos==0 && extra==0 && weight<INT_MAX) fprintf(output,"Graph OK.\n"); } if (asymm || refl || nonpos || extra || weight>=INT_MAX) return FALSE; return TRUE; }
/* * clique_find_all() * * Find all cliques with weight at least min_weight and at most max_weight. * * g - the graph * min_weight - minimum weight of cliques to search for. If min_weight==0, * searches for maximum weight cliques. * max_weight - maximum weight of cliques to search for. If max_weight==0, * no upper limit is used. If min_weight==0, max_weight must * also be 0. * maximal - require cliques to be maximal cliques * opts - time printing and clique storage options * * Returns the number of cliques found. This can be less than the number * of cliques in the graph iff opts->time_function() or opts->user_function() * returns FALSE (request abort). * * The cliques found are stored in opts->clique_list[] and * opts->user_function() is called with them (if non-NULL). The cliques * stored in opts->clique_list[] are newly allocated, and can be freed * by set_free(). * * Note: Automatically uses clique_unweighted_find_all if all vertex * weights are the same. */ int clique_find_all(graph_t *g, int min_weight, int max_weight, boolean maximal, clique_options *opts) { int i,n; int *table; ENTRANCE_SAVE(); entrance_level++; if (opts==NULL) opts=clique_default_options; ASSERT((sizeof(setelement)*8)==ELEMENTSIZE); ASSERT(g!=NULL); ASSERT(min_weight>=0); ASSERT(max_weight>=0); ASSERT((max_weight==0) || (min_weight <= max_weight)); ASSERT(!((min_weight==0) && (max_weight>0))); ASSERT((opts->reorder_function==NULL) || (opts->reorder_map==NULL)); if ((max_weight>0) && (min_weight>max_weight)) { /* state was not changed */ entrance_level--; return 0; } if (clocks_per_sec==0) clocks_per_sec=sysconf(_SC_CLK_TCK); ASSERT(clocks_per_sec>0); if (!graph_weighted(g)) { min_weight=DIV_UP(min_weight,g->weights[0]); if (max_weight) { max_weight=DIV_DOWN(max_weight,g->weights[0]); if (max_weight < min_weight) { /* state was not changed */ entrance_level--; return 0; } } weight_multiplier = g->weights[0]; entrance_level--; i=clique_unweighted_find_all(g,min_weight,max_weight,maximal, opts); ENTRANCE_RESTORE(); return i; } /* Dynamic allocation */ current_clique=set_new(g->n); best_clique=set_new(g->n); clique_size=malloc(g->n * sizeof(int)); memset(clique_size, 0, g->n * sizeof(int)); /* table allocated later */ temp_list=malloc((g->n+2)*sizeof(int *)); temp_count=0; /* "start clock" */ gettimeofday(&realtimer,NULL); times(&cputimer); /* reorder */ if (opts->reorder_function) { table=opts->reorder_function(g,TRUE); } else if (opts->reorder_map) { table=reorder_duplicate(opts->reorder_map,g->n); } else { table=reorder_ident(g->n); } ASSERT(reorder_is_bijection(table,g->n)); /* First phase */ n=weighted_clique_search_single(table,min_weight,INT_MAX,g,opts); if (n==0) { /* Requested clique has not been found. */ goto cleanreturn; } if (min_weight==0) { min_weight=n; max_weight=n; maximal=FALSE; /* They're maximum cliques already. */ } if (max_weight==0) max_weight=INT_MAX; for (i=0; i < g->n; i++) if ((clique_size[table[i]] >= min_weight) || (clique_size[table[i]] == 0)) break; /* Second phase */ n=weighted_clique_search_all(table,i,min_weight,max_weight,maximal, g,opts); cleanreturn: /* Free resources */ for (i=0; i < temp_count; i++) free(temp_list[i]); free(temp_list); free(table); set_free(current_clique); set_free(best_clique); free(clique_size); ENTRANCE_RESTORE(); entrance_level--; return n; }
/* * graph_print() * * Prints a representation of the graph g to stdout (along with any errors * noticed). Mainly useful for debugging purposes and trivial output. * * The output consists of a first line describing the dimensions and then * one line per vertex containing the vertex number (numbered 0,...,n-1), * the vertex weight (if the graph is weighted), "->" and then a list * of all vertices it is adjacent to. */ void graph_print(graph_t *g) { int i,j; int asymm=0; int refl=0; int nonpos=0; int extra=0; unsigned int weight=0; boolean weighted; ASSERT((sizeof(setelement)*8)==ELEMENTSIZE); if (g==NULL) { printf(" WARNING: Graph pointer is NULL!\n"); return; } if (g->n <= 0) { printf(" WARNING: Graph has %d vertices " "(should be positive)!\n",g->n); return; } weighted=graph_weighted(g); printf("%s graph has %d vertices, %d edges (density %.2f).\n", weighted?"Weighted":((g->weights[0]==1)? "Unweighted":"Semi-weighted"), g->n,graph_edge_count(g), (float)graph_edge_count(g)/((float)(g->n - 1)*(g->n)/2)); for (i=0; i < g->n; i++) { printf("%2d",i); if (weighted) { printf(" w=%d",g->weights[i]); if (g->weights[i] <= 0) { printf("*NON-POSITIVE*"); nonpos++; } } if (weight < INT_MAX) weight+=g->weights[i]; printf(" ->"); for (j=0; j < g->n; j++) { if (SET_CONTAINS_FAST(g->edges[i],j)) { printf(" %d",j); if (i==j) { printf("*REFLEXIVE*"); refl++; } if (!SET_CONTAINS_FAST(g->edges[j],i)) { printf("*ASYMMERTIC*"); asymm++; } } } for (j=g->n; j < SET_ARRAY_LENGTH(g->edges[i])*ELEMENTSIZE; j++) { if (SET_CONTAINS_FAST(g->edges[i],j)) { printf(" %d*NON-EXISTENT*",j); extra++; } } printf("\n"); } if (asymm) printf(" WARNING: Graph contained %d asymmetric edges!\n", asymm); if (refl) printf(" WARNING: Graph contained %d reflexive edges!\n", refl); if (nonpos) printf(" WARNING: Graph contained %d non-positive vertex " "weights!\n",nonpos); if (extra) printf(" WARNING: Graph contained %d edges to " "non-existent vertices!\n",extra); if (weight>=INT_MAX) printf(" WARNING: Total graph weight >= INT_MAX!\n"); return; }
/* * clique_find_single() * * Returns a clique with weight at least min_weight and at most max_weight. * * g - the graph * min_weight - minimum weight of clique to search for. If min_weight==0, * searches for a maximum weight clique. * max_weight - maximum weight of clique to search for. If max_weight==0, * no upper limit is used. If min_weight==0, max_weight must * also be 0. * maximal - require returned clique to be maximal * opts - time printing options * * Returns the set of vertices forming the clique, or NULL if a clique * of requested weight/maximality does not exist in the graph (or if * opts->time_function() requests abort). * * The returned clique is newly allocated and can be freed by set_free(). * * Note: Does NOT use opts->user_function() or opts->clique_list[]. * Note: Automatically uses clique_unweighted_find_single if all vertex * weights are the same. */ set_t clique_find_single(graph_t *g,int min_weight,int max_weight, boolean maximal, clique_options *opts) { int i; int *table; set_t s; ENTRANCE_SAVE(); entrance_level++; if (opts==NULL) opts=clique_default_options; ASSERT((sizeof(setelement)*8)==ELEMENTSIZE); ASSERT(g!=NULL); ASSERT(min_weight>=0); ASSERT(max_weight>=0); ASSERT((max_weight==0) || (min_weight <= max_weight)); ASSERT(!((min_weight==0) && (max_weight>0))); ASSERT((opts->reorder_function==NULL) || (opts->reorder_map==NULL)); if ((max_weight>0) && (min_weight>max_weight)) { /* state was not changed */ entrance_level--; return NULL; } if (clocks_per_sec==0) clocks_per_sec=sysconf(_SC_CLK_TCK); ASSERT(clocks_per_sec>0); /* Check whether we can use unweighted routines. */ if (!graph_weighted(g)) { min_weight=DIV_UP(min_weight,g->weights[0]); if (max_weight) { max_weight=DIV_DOWN(max_weight,g->weights[0]); if (max_weight < min_weight) { /* state was not changed */ entrance_level--; return NULL; } } weight_multiplier = g->weights[0]; entrance_level--; s=clique_unweighted_find_single(g,min_weight,max_weight, maximal,opts); ENTRANCE_RESTORE(); return s; } /* Dynamic allocation */ current_clique=set_new(g->n); best_clique=set_new(g->n); clique_size=malloc(g->n * sizeof(int)); memset(clique_size, 0, g->n * sizeof(int)); /* table allocated later */ temp_list=malloc((g->n+2)*sizeof(int *)); temp_count=0; clique_list_count=0; /* "start clock" */ gettimeofday(&realtimer,NULL); times(&cputimer); /* reorder */ if (opts->reorder_function) { table=opts->reorder_function(g,TRUE); } else if (opts->reorder_map) { table=reorder_duplicate(opts->reorder_map,g->n); } else { table=reorder_ident(g->n); } ASSERT(reorder_is_bijection(table,g->n)); if (max_weight==0) max_weight=INT_MAX; if (weighted_clique_search_single(table,min_weight,max_weight, g,opts)==0) { /* Requested clique has not been found. */ set_free(best_clique); best_clique=NULL; goto cleanreturn; } if (maximal && (min_weight>0)) { maximalize_clique(best_clique,g); if (graph_subgraph_weight(g,best_clique) > max_weight) { clique_options localopts; localopts.time_function = opts->time_function; localopts.output = opts->output; localopts.user_function = false_function; localopts.clique_list = &best_clique; localopts.clique_list_length = 1; for (i=0; i < g->n-1; i++) if ((clique_size[table[i]] >= min_weight) || (clique_size[table[i]] == 0)) break; if (!weighted_clique_search_all(table,i,min_weight, max_weight,maximal, g,&localopts)) { set_free(best_clique); best_clique=NULL; } } } cleanreturn: s=best_clique; /* Free resources */ for (i=0; i < temp_count; i++) free(temp_list[i]); free(temp_list); temp_list=NULL; temp_count=0; free(table); set_free(current_clique); current_clique=NULL; free(clique_size); clique_size=NULL; ENTRANCE_RESTORE(); entrance_level--; return s; }