summaryrefslogtreecommitdiff
path: root/graph-checker/nauty/nautycliquer.c
diff options
context:
space:
mode:
Diffstat (limited to 'graph-checker/nauty/nautycliquer.c')
-rw-r--r--graph-checker/nauty/nautycliquer.c2665
1 files changed, 2665 insertions, 0 deletions
diff --git a/graph-checker/nauty/nautycliquer.c b/graph-checker/nauty/nautycliquer.c
new file mode 100644
index 0000000..9c8d531
--- /dev/null
+++ b/graph-checker/nauty/nautycliquer.c
@@ -0,0 +1,2665 @@
+/* This file is a concatenation of the files cliquer.c, graph.c
+ and reorder.c from cliquer-1.21 except the routines for
+ reading and writing dimacs files.
+
+ Also some timing code is commented out because it is not used
+ by nauty and causes portability problem on non-Unix systems
+ (thanks to Isuru Fernando).
+
+ Some procedures which call cliquer with nauty-format graph
+ are added. Apart from removing DIMACS, the cliquer procedures
+ have not been changed except to include nautycliquer.h in
+ place of the previously included files.
+
+ cliquer was kindly provided by Sampo Nisjkanen and Patric Ostergard.
+
+ Brendan McKay. Aug 27, 2016
+*/
+
+#include "nautycliquer.h"
+
+/*
+ * This file contains the clique searching routines.
+ *
+ * Copyright (C) 2002 Sampo Niskanen, Patric Östergård.
+ * Licensed under the GNU GPL, read the file LICENSE for details.
+ * This version covered by nauty&Traces licence, see file COPYRIGHT.
+ */
+
+/*
+#include <stdio.h>
+#include <stdlib.h>
+#include <limits.h>
+#include <unistd.h>
+#include <sys/time.h>
+#include <sys/times.h>
+
+#include "cliquer.h"
+*/
+
+
+/* Default cliquer options; no TLS_ATTR on this one */
+static clique_options clique_default_options_struct = {
+ reorder_by_default, NULL, clique_print_time, NULL, NULL, NULL, NULL, 0
+};
+clique_options *clique_default_options=&clique_default_options_struct;
+
+
+/* Calculate d/q, rounding result upwards/downwards. */
+#define DIV_UP(d,q) (((d)+(q)-1)/(q))
+#define DIV_DOWN(d,q) ((int)((d)/(q)))
+
+
+/* Global variables used: */
+/* These must be saved and restored in re-entrance. */
+static TLS_ATTR int *clique_size; /* c[i] == max. clique size in {0,1,...,i-1} */
+static TLS_ATTR set_t current_clique; /* Current clique being searched. */
+static TLS_ATTR set_t best_clique; /* Largest/heaviest clique found so far. */
+#if 0
+static struct tms cputimer; /* Timer for opts->time_function() */
+static struct timeval realtimer; /* Timer for opts->time_function() */
+#endif
+static TLS_ATTR int clique_list_count=0; /* No. of cliques in opts->clique_list[] */
+static TLS_ATTR int weight_multiplier=1; /* Weights multiplied by this when passing
+ * to time_function(). */
+
+/* List cache (contains memory blocks of size g->n * sizeof(int)) */
+static TLS_ATTR int **temp_list=NULL;
+static TLS_ATTR int temp_count=0;
+
+
+/*
+ * Macros for re-entrance. ENTRANCE_SAVE() must be called immediately
+ * after variable definitions, ENTRANCE_RESTORE() restores global
+ * variables to original values. entrance_level should be increased
+ * and decreased accordingly.
+ */
+static int entrance_level=0; /* How many levels for entrance have occurred? */
+
+#define ENTRANCE_SAVE() \
+int *old_clique_size = clique_size; \
+set_t old_current_clique = current_clique; \
+set_t old_best_clique = best_clique; \
+int old_clique_list_count = clique_list_count; \
+int old_weight_multiplier = weight_multiplier; \
+int **old_temp_list = temp_list; \
+int old_temp_count = temp_count;
+/*
+struct tms old_cputimer; \
+struct timeval old_realtimer; \
+memcpy(&old_cputimer,&cputimer,sizeof(struct tms)); \
+memcpy(&old_realtimer,&realtimer,sizeof(struct timeval))
+*/
+
+#define ENTRANCE_RESTORE() \
+clique_size = old_clique_size; \
+current_clique = old_current_clique; \
+best_clique = old_best_clique; \
+clique_list_count = old_clique_list_count; \
+weight_multiplier = old_weight_multiplier; \
+temp_list = old_temp_list;
+/*
+temp_count = old_temp_count; \
+memcpy(&cputimer,&old_cputimer,sizeof(struct tms)); \
+memcpy(&realtimer,&old_realtimer,sizeof(struct timeval));
+temp_count = old_temp_count;
+*/
+
+
+/* Number of clock ticks per second (as returned by sysconf(_SC_CLK_TCK)) */
+static int clocks_per_sec=0;
+
+
+
+/* Recursion and helper functions */
+static boolean sub_unweighted_single(int *table, int size, int min_size,
+ graph_t *g);
+static int sub_unweighted_all(int *table, int size, int min_size, int max_size,
+ boolean maximal, graph_t *g,
+ clique_options *opts);
+static int sub_weighted_all(int *table, int size, int weight,
+ int current_weight, int prune_low, int prune_high,
+ int min_weight, int max_weight, boolean maximal,
+ graph_t *g, clique_options *opts);
+
+
+static boolean store_clique(set_t clique, graph_t *g, clique_options *opts);
+static boolean is_maximal(set_t clique, graph_t *g);
+static boolean false_function(set_t clique,graph_t *g,clique_options *opts);
+
+/***** Unweighted searches *****/
+/*
+ * Unweighted searches are done separately from weighted searches because
+ * some effective pruning methods can be used when the vertex weights
+ * are all 1. Single and all clique finding routines are separated,
+ * because the single clique finding routine can store the found clique
+ * while it is returning from the recursion, thus requiring no implicit
+ * storing of the current clique. When searching for all cliques the
+ * current clique must be stored.
+ */
+
+
+/*
+ * unweighted_clique_search_single()
+ *
+ * Searches for a single clique of size min_size. Stores maximum clique
+ * sizes into clique_size[].
+ *
+ * table - the order of the vertices in g to use
+ * min_size - minimum size of clique to search for. If min_size==0,
+ * searches for a maximum clique.
+ * g - the graph
+ * opts - time printing options
+ *
+ * opts->time_function is called after each base-level recursion, if
+ * non-NULL. (NOT IN THIS VERSION)
+ *
+ * Returns the size of the clique found, or 0 if min_size>0 and a clique
+ * of that size was not found (or if time_function aborted the search).
+ * The largest clique found is stored in current_clique.
+ *
+ * Note: Does NOT use opts->user_function of opts->clique_list.
+ */
+static int unweighted_clique_search_single(int *table, int min_size,
+ graph_t *g, clique_options *opts) {
+#if 0
+ struct tms tms;
+ struct timeval timeval;
+#endif
+ int i,j;
+ int v,w;
+ int *newtable;
+ int newsize;
+
+ v=table[0];
+ clique_size[v]=1;
+ set_empty(current_clique);
+ SET_ADD_ELEMENT(current_clique,v);
+ if (min_size==1)
+ return 1;
+
+ if (temp_count) {
+ temp_count--;
+ newtable=temp_list[temp_count];
+ } else {
+ newtable=malloc(g->n * sizeof(int));
+ }
+ for (i=1; i < g->n; i++) {
+ w=v;
+ v=table[i];
+
+ newsize=0;
+ for (j=0; j<i; j++) {
+ if (GRAPH_IS_EDGE(g, v, table[j])) {
+ newtable[newsize]=table[j];
+ newsize++;
+ }
+ }
+
+ if (sub_unweighted_single(newtable,newsize,clique_size[w],g)) {
+ SET_ADD_ELEMENT(current_clique,v);
+ clique_size[v]=clique_size[w]+1;
+ } else {
+ clique_size[v]=clique_size[w];
+ }
+
+#if 0
+ if (opts && opts->time_function) {
+ gettimeofday(&timeval,NULL);
+ times(&tms);
+ if (!opts->time_function(entrance_level,
+ i+1,g->n,clique_size[v] *
+ weight_multiplier,
+ (double)(tms.tms_utime-
+ cputimer.tms_utime)/
+ clocks_per_sec,
+ timeval.tv_sec-
+ realtimer.tv_sec+
+ (double)(timeval.tv_usec-
+ realtimer.tv_usec)/
+ 1000000,opts)) {
+ temp_list[temp_count++]=newtable;
+ return 0;
+ }
+ }
+#endif
+
+ if (min_size) {
+ if (clique_size[v]>=min_size) {
+ temp_list[temp_count++]=newtable;
+ return clique_size[v];
+ }
+ if (clique_size[v]+g->n-i-1 < min_size) {
+ temp_list[temp_count++]=newtable;
+ return 0;
+ }
+ }
+ }
+
+ temp_list[temp_count++]=newtable;
+
+ if (min_size)
+ return 0;
+ return clique_size[v];
+}
+
+/*
+ * sub_unweighted_single()
+ *
+ * Recursion function for searching for a single clique of size min_size.
+ *
+ * table - subset of the vertices in graph
+ * size - size of table
+ * min_size - size of clique to look for within the subgraph
+ * (decreased with every recursion)
+ * g - the graph
+ *
+ * Returns TRUE if a clique of size min_size is found, FALSE otherwise.
+ * If a clique of size min_size is found, it is stored in current_clique.
+ *
+ * clique_size[] for all values in table must be defined and correct,
+ * otherwise inaccurate results may occur.
+ */
+static boolean sub_unweighted_single(int *table, int size, int min_size,
+ graph_t *g) {
+ int i;
+ int v;
+ int *newtable;
+ int *p1, *p2;
+
+ /* Zero or one vertices needed anymore. */
+ if (min_size <= 1) {
+ if (size>0 && min_size==1) {
+ set_empty(current_clique);
+ SET_ADD_ELEMENT(current_clique,table[0]);
+ return TRUE;
+ }
+ if (min_size==0) {
+ set_empty(current_clique);
+ return TRUE;
+ }
+ return FALSE;
+ }
+ if (size < min_size)
+ return FALSE;
+
+ /* Dynamic memory allocation with cache */
+ if (temp_count) {
+ temp_count--;
+ newtable=temp_list[temp_count];
+ } else {
+ newtable=malloc(g->n * sizeof(int));
+ }
+
+ for (i = size-1; i >= 0; i--) {
+ v = table[i];
+
+ if (clique_size[v] < min_size)
+ break;
+ /* This is faster when compiling with gcc than placing
+ * this in the for-loop condition. */
+ if (i+1 < min_size)
+ break;
+
+ /* Very ugly code, but works faster than "for (i=...)" */
+ p1 = newtable;
+ for (p2=table; p2 < table+i; p2++) {
+ int w = *p2;
+ if (GRAPH_IS_EDGE(g, v, w)) {
+ *p1 = w;
+ p1++;
+ }
+ }
+
+ /* Avoid unneccessary loops (next size == p1-newtable) */
+ if (p1-newtable < min_size-1)
+ continue;
+ /* Now p1-newtable >= min_size-1 >= 2-1 == 1, so we can use
+ * p1-newtable-1 safely. */
+ if (clique_size[newtable[p1-newtable-1]] < min_size-1)
+ continue;
+
+ if (sub_unweighted_single(newtable,p1-newtable,
+ min_size-1,g)) {
+ /* Clique found. */
+ SET_ADD_ELEMENT(current_clique,v);
+ temp_list[temp_count++]=newtable;
+ return TRUE;
+ }
+ }
+ temp_list[temp_count++]=newtable;
+ return FALSE;
+}
+
+
+/*
+ * unweighted_clique_search_all()
+ *
+ * Searches for all cliques with size at least min_size and at most
+ * max_size. Stores the cliques as opts declares.
+ *
+ * table - the order of the vertices in g to search
+ * start - first index where the subgraph table[0], ..., table[start]
+ * might include a requested kind of clique
+ * min_size - minimum size of clique to search for. min_size > 0 !
+ * max_size - maximum size of clique to search for. If no upper limit
+ * is desired, use eg. INT_MAX
+ * maximal - requires cliques to be maximal
+ * g - the graph
+ * opts - time printing and clique storage options
+ *
+ * Cliques found are stored as defined by opts->user_function and
+ * opts->clique_list. opts->time_function is called after each
+ * base-level recursion, if non-NULL.
+ *
+ * clique_size[] must be defined and correct for all values of
+ * table[0], ..., table[start-1].
+ *
+ * Returns the number of cliques stored (not neccessarily number of cliques
+ * in graph, if user/time_function aborts).
+ */
+static int unweighted_clique_search_all(int *table, int start,
+ int min_size, int max_size,
+ boolean maximal, graph_t *g,
+ clique_options *opts) {
+#if 0
+ struct timeval timeval;
+ struct tms tms;
+#endif
+ int i,j;
+ int v;
+ int *newtable;
+ int newsize;
+ int count=0;
+
+ if (temp_count) {
+ temp_count--;
+ newtable=temp_list[temp_count];
+ } else {
+ newtable=malloc(g->n * sizeof(int));
+ }
+
+ clique_list_count=0;
+ set_empty(current_clique);
+ for (i=start; i < g->n; i++) {
+ v=table[i];
+ clique_size[v]=min_size; /* Do not prune here. */
+
+ newsize=0;
+ for (j=0; j<i; j++) {
+ if (GRAPH_IS_EDGE(g,v,table[j])) {
+ newtable[newsize]=table[j];
+ newsize++;
+ }
+ }
+
+ SET_ADD_ELEMENT(current_clique,v);
+ j=sub_unweighted_all(newtable,newsize,min_size-1,max_size-1,
+ maximal,g,opts);
+ SET_DEL_ELEMENT(current_clique,v);
+ if (j<0) {
+ /* Abort. */
+ count-=j;
+ break;
+ }
+ count+=j;
+
+#if 0
+ if (opts->time_function) {
+ gettimeofday(&timeval,NULL);
+ times(&tms);
+ if (!opts->time_function(entrance_level,
+ i+1,g->n,min_size *
+ weight_multiplier,
+ (double)(tms.tms_utime-
+ cputimer.tms_utime)/
+ clocks_per_sec,
+ timeval.tv_sec-
+ realtimer.tv_sec+
+ (double)(timeval.tv_usec-
+ realtimer.tv_usec)/
+ 1000000,opts)) {
+ /* Abort. */
+ break;
+ }
+ }
+#endif
+ }
+ temp_list[temp_count++]=newtable;
+ return count;
+}
+
+/*
+ * sub_unweighted_all()
+ *
+ * Recursion function for searching for all cliques of given size.
+ *
+ * table - subset of vertices of graph g
+ * size - size of table
+ * min_size - minimum size of cliques to search for (decreased with
+ * every recursion)
+ * max_size - maximum size of cliques to search for (decreased with
+ * every recursion). If no upper limit is desired, use
+ * eg. INT_MAX
+ * maximal - require cliques to be maximal (passed through)
+ * g - the graph
+ * opts - storage options
+ *
+ * All cliques of suitable size found are stored according to opts.
+ *
+ * Returns the number of cliques found. If user_function returns FALSE,
+ * then the number of cliques is returned negative.
+ *
+ * Uses current_clique to store the currently-being-searched clique.
+ * clique_size[] for all values in table must be defined and correct,
+ * otherwise inaccurate results may occur.
+ */
+static int sub_unweighted_all(int *table, int size, int min_size, int max_size,
+ boolean maximal, graph_t *g,
+ clique_options *opts) {
+ int i;
+ int v;
+ int n;
+ int *newtable;
+ int *p1, *p2;
+ int count=0; /* Amount of cliques found */
+
+ if (min_size <= 0) {
+ if ((!maximal) || is_maximal(current_clique,g)) {
+ /* We've found one. Store it. */
+ count++;
+ if (!store_clique(current_clique,g,opts)) {
+ return -count;
+ }
+ }
+ if (max_size <= 0) {
+ /* If we add another element, size will be too big. */
+ return count;
+ }
+ }
+
+ if (size < min_size) {
+ return count;
+ }
+
+ /* Dynamic memory allocation with cache */
+ if (temp_count) {
+ temp_count--;
+ newtable=temp_list[temp_count];
+ } else {
+ newtable=malloc(g->n * sizeof(int));
+ }
+
+ for (i=size-1; i>=0; i--) {
+ v = table[i];
+ if (clique_size[v] < min_size) {
+ break;
+ }
+ if (i+1 < min_size) {
+ break;
+ }
+
+ /* Very ugly code, but works faster than "for (i=...)" */
+ p1 = newtable;
+ for (p2=table; p2 < table+i; p2++) {
+ int w = *p2;
+ if (GRAPH_IS_EDGE(g, v, w)) {
+ *p1 = w;
+ p1++;
+ }
+ }
+
+ /* Avoid unneccessary loops (next size == p1-newtable) */
+ if (p1-newtable < min_size-1) {
+ continue;
+ }
+
+ SET_ADD_ELEMENT(current_clique,v);
+ n=sub_unweighted_all(newtable,p1-newtable,
+ min_size-1,max_size-1,maximal,g,opts);
+ SET_DEL_ELEMENT(current_clique,v);
+ if (n < 0) {
+ /* Abort. */
+ count -= n;
+ count = -count;
+ break;
+ }
+ count+=n;
+ }
+ temp_list[temp_count++]=newtable;
+ return count;
+}
+
+
+
+
+/***** Weighted clique searches *****/
+/*
+ * Weighted clique searches can use the same recursive routine, because
+ * in both cases (single/all) they have to search through all potential
+ * permutations searching for heavier cliques.
+ */
+
+
+/*
+ * weighted_clique_search_single()
+ *
+ * Searches for a single clique of weight at least min_weight, and at
+ * most max_weight. Stores maximum clique sizes into clique_size[]
+ * (or min_weight-1, whichever is smaller).
+ *
+ * table - the order of the vertices in g to use
+ * min_weight - minimum weight of clique to search for. If min_weight==0,
+ * then searches for a maximum weight clique
+ * max_weight - maximum weight of clique to search for. If no upper limit
+ * is desired, use eg. INT_MAX
+ * g - the graph
+ * opts - time printing options
+ *
+ * opts->time_function is called after each base-level recursion, if
+ * non-NULL.
+ *
+ * Returns 0 if a clique of requested weight was not found (also if
+ * time_function requested an abort), otherwise returns >= 1.
+ * If min_weight==0 (search for maximum-weight clique), then the return
+ * value is the weight of the clique found. The found clique is stored
+ * in best_clique.
+ *
+ * Note: Does NOT use opts->user_function of opts->clique_list.
+ */
+static int weighted_clique_search_single(int *table, int min_weight,
+ int max_weight, graph_t *g,
+ clique_options *opts) {
+#if 0
+ struct timeval timeval;
+ struct tms tms;
+#endif
+ int i,j;
+ int v;
+ int *newtable;
+ int newsize;
+ int newweight;
+ int search_weight;
+ int min_w;
+ clique_options localopts;
+
+ if (min_weight==0)
+ min_w=INT_MAX;
+ else
+ min_w=min_weight;
+
+
+ if (min_weight==1) {
+ /* min_weight==1 may cause trouble in the routine, and
+ * it's trivial to check as it's own case.
+ * We write nothing to clique_size[]. */
+ for (i=0; i < g->n; i++) {
+ if (g->weights[table[i]] <= max_weight) {
+ set_empty(best_clique);
+ SET_ADD_ELEMENT(best_clique,table[i]);
+ return g->weights[table[i]];
+ }
+ }
+ return 0;
+ }
+
+ localopts.time_function=NULL;
+ localopts.reorder_function=NULL;
+ localopts.reorder_map=NULL;
+ localopts.user_function=false_function;
+ localopts.user_data=NULL;
+ localopts.clique_list=&best_clique;
+ localopts.clique_list_length=1;
+ clique_list_count=0;
+
+ v=table[0];
+ set_empty(best_clique);
+ SET_ADD_ELEMENT(best_clique,v);
+ search_weight=g->weights[v];
+ if (min_weight && (search_weight >= min_weight)) {
+ if (search_weight <= max_weight) {
+ /* Found suitable clique. */
+ return search_weight;
+ }
+ search_weight=min_weight-1;
+ }
+ clique_size[v]=search_weight;
+ set_empty(current_clique);
+
+ if (temp_count) {
+ temp_count--;
+ newtable=temp_list[temp_count];
+ } else {
+ newtable=malloc(g->n * sizeof(int));
+ }
+
+ for (i = 1; i < g->n; i++) {
+ v=table[i];
+
+ newsize=0;
+ newweight=0;
+ for (j=0; j<i; j++) {
+ if (GRAPH_IS_EDGE(g,v,table[j])) {
+ newweight += g->weights[table[j]];
+ newtable[newsize]=table[j];
+ newsize++;
+ }
+ }
+
+
+ SET_ADD_ELEMENT(current_clique,v);
+ search_weight=sub_weighted_all(newtable,newsize,newweight,
+ g->weights[v],search_weight,
+ clique_size[table[i-1]] +
+ g->weights[v],
+ min_w,max_weight,FALSE,
+ g,&localopts);
+ SET_DEL_ELEMENT(current_clique,v);
+ if (search_weight < 0) {
+ break;
+ }
+
+ clique_size[v]=search_weight;
+
+#if 0
+ if (opts->time_function) {
+ gettimeofday(&timeval,NULL);
+ times(&tms);
+ if (!opts->time_function(entrance_level,
+ i+1,g->n,clique_size[v] *
+ weight_multiplier,
+ (double)(tms.tms_utime-
+ cputimer.tms_utime)/
+ clocks_per_sec,
+ timeval.tv_sec-
+ realtimer.tv_sec+
+ (double)(timeval.tv_usec-
+ realtimer.tv_usec)/
+ 1000000,opts)) {
+ set_free(current_clique);
+ current_clique=NULL;
+ break;
+ }
+ }
+#endif
+ }
+ temp_list[temp_count++]=newtable;
+ if (min_weight && (search_weight > 0)) {
+ /* Requested clique has not been found. */
+ return 0;
+ }
+ return clique_size[table[i-1]];
+}
+
+
+/*
+ * weighted_clique_search_all()
+ *
+ * Searches for all cliques with weight at least min_weight and at most
+ * max_weight. Stores the cliques as opts declares.
+ *
+ * table - the order of the vertices in g to search
+ * start - first index where the subgraph table[0], ..., table[start]
+ * might include a requested kind of clique
+ * min_weight - minimum weight of clique to search for. min_weight > 0 !
+ * max_weight - maximum weight of clique to search for. If no upper limit
+ * is desired, use eg. INT_MAX
+ * maximal - search only for maximal cliques
+ * g - the graph
+ * opts - time printing and clique storage options
+ *
+ * Cliques found are stored as defined by opts->user_function and
+ * opts->clique_list. opts->time_function is called after each
+ * base-level recursion, if non-NULL.
+ *
+ * clique_size[] must be defined and correct for all values of
+ * table[0], ..., table[start-1].
+ *
+ * Returns the number of cliques stored (not neccessarily number of cliques
+ * in graph, if user/time_function aborts).
+ */
+static int weighted_clique_search_all(int *table, int start,
+ int min_weight, int max_weight,
+ boolean maximal, graph_t *g,
+ clique_options *opts) {
+#if 0
+ struct timeval timeval;
+ struct tms tms;
+#endif
+ int i,j;
+ int v;
+ int *newtable;
+ int newsize;
+ int newweight;
+
+ if (temp_count) {
+ temp_count--;
+ newtable=temp_list[temp_count];
+ } else {
+ newtable=malloc(g->n * sizeof(int));
+ }
+
+ clique_list_count=0;
+ set_empty(current_clique);
+ for (i=start; i < g->n; i++) {
+ v=table[i];
+ clique_size[v]=min_weight; /* Do not prune here. */
+
+ newsize=0;
+ newweight=0;
+ for (j=0; j<i; j++) {
+ if (GRAPH_IS_EDGE(g,v,table[j])) {
+ newtable[newsize]=table[j];
+ newweight+=g->weights[table[j]];
+ newsize++;
+ }
+ }
+
+ SET_ADD_ELEMENT(current_clique,v);
+ j=sub_weighted_all(newtable,newsize,newweight,
+ g->weights[v],min_weight-1,INT_MAX,
+ min_weight,max_weight,maximal,g,opts);
+ SET_DEL_ELEMENT(current_clique,v);
+
+ if (j<0) {
+ /* Abort. */
+ break;
+ }
+
+#if 0
+ if (opts->time_function) {
+ gettimeofday(&timeval,NULL);
+ times(&tms);
+ if (!opts->time_function(entrance_level,
+ i+1,g->n,clique_size[v] *
+ weight_multiplier,
+ (double)(tms.tms_utime-
+ cputimer.tms_utime)/
+ clocks_per_sec,
+ timeval.tv_sec-
+ realtimer.tv_sec+
+ (double)(timeval.tv_usec-
+ realtimer.tv_usec)/
+ 1000000,opts)) {
+ set_free(current_clique);
+ current_clique=NULL;
+ break;
+ }
+ }
+#endif
+ }
+ temp_list[temp_count++]=newtable;
+
+ return clique_list_count;
+}
+
+/*
+ * sub_weighted_all()
+ *
+ * Recursion function for searching for all cliques of given weight.
+ *
+ * table - subset of vertices of graph g
+ * size - size of table
+ * weight - total weight of vertices in table
+ * current_weight - weight of clique found so far
+ * prune_low - ignore all cliques with weight less or equal to this value
+ * (often heaviest clique found so far) (passed through)
+ * prune_high - maximum weight possible for clique in this subgraph
+ * (passed through)
+ * min_size - minimum weight of cliques to search for (passed through)
+ * Must be greater than 0.
+ * max_size - maximum weight of cliques to search for (passed through)
+ * If no upper limit is desired, use eg. INT_MAX
+ * maximal - search only for maximal cliques
+ * g - the graph
+ * opts - storage options
+ *
+ * All cliques of suitable weight found are stored according to opts.
+ *
+ * Returns weight of heaviest clique found (prune_low if a heavier clique
+ * hasn't been found); if a clique with weight at least min_size is found
+ * then min_size-1 is returned. If clique storage failed, -1 is returned.
+ *
+ * The largest clique found smaller than max_weight is stored in
+ * best_clique, if non-NULL.
+ *
+ * Uses current_clique to store the currently-being-searched clique.
+ * clique_size[] for all values in table must be defined and correct,
+ * otherwise inaccurate results may occur.
+ *
+ * To search for a single maximum clique, use min_weight==max_weight==INT_MAX,
+ * with best_clique non-NULL. To search for a single given-weight clique,
+ * use opts->clique_list and opts->user_function=false_function. When
+ * searching for all cliques, min_weight should be given the minimum weight
+ * desired.
+ */
+static int sub_weighted_all(int *table, int size, int weight,
+ int current_weight, int prune_low, int prune_high,
+ int min_weight, int max_weight, boolean maximal,
+ graph_t *g, clique_options *opts) {
+ int i;
+ int v,w;
+ int *newtable;
+ int *p1, *p2;
+ int newweight;
+
+ if (current_weight >= min_weight) {
+ if ((current_weight <= max_weight) &&
+ ((!maximal) || is_maximal(current_clique,g))) {
+ /* We've found one. Store it. */
+ if (!store_clique(current_clique,g,opts)) {
+ return -1;
+ }
+ }
+ if (current_weight >= max_weight) {
+ /* Clique too heavy. */
+ return min_weight-1;
+ }
+ }
+ if (size <= 0) {
+ /* current_weight < min_weight, prune_low < min_weight,
+ * so return value is always < min_weight. */
+ if (current_weight>prune_low) {
+ if (best_clique)
+ set_copy(best_clique,current_clique);
+ if (current_weight < min_weight)
+ return current_weight;
+ else
+ return min_weight-1;
+ } else {
+ return prune_low;
+ }
+ }
+
+ /* Dynamic memory allocation with cache */
+ if (temp_count) {
+ temp_count--;
+ newtable=temp_list[temp_count];
+ } else {
+ newtable=malloc(g->n * sizeof(int));
+ }
+
+ for (i = size-1; i >= 0; i--) {
+ v = table[i];
+ if (current_weight+clique_size[v] <= prune_low) {
+ /* Dealing with subset without heavy enough clique. */
+ break;
+ }
+ if (current_weight+weight <= prune_low) {
+ /* Even if all elements are added, won't do. */
+ break;
+ }
+
+ /* Very ugly code, but works faster than "for (i=...)" */
+ p1 = newtable;
+ newweight = 0;
+ for (p2=table; p2 < table+i; p2++) {
+ w = *p2;
+ if (GRAPH_IS_EDGE(g, v, w)) {
+ *p1 = w;
+ newweight += g->weights[w];
+ p1++;
+ }
+ }
+
+ w=g->weights[v];
+ weight-=w;
+ /* Avoid a few unneccessary loops */
+ if (current_weight+w+newweight <= prune_low) {
+ continue;
+ }
+
+ SET_ADD_ELEMENT(current_clique,v);
+ prune_low=sub_weighted_all(newtable,p1-newtable,
+ newweight,
+ current_weight+w,
+ prune_low,prune_high,
+ min_weight,max_weight,maximal,
+ g,opts);
+ SET_DEL_ELEMENT(current_clique,v);
+ if ((prune_low<0) || (prune_low>=prune_high)) {
+ /* Impossible to find larger clique. */
+ break;
+ }
+ }
+ temp_list[temp_count++]=newtable;
+ return prune_low;
+}
+
+
+
+
+/***** Helper functions *****/
+
+
+/*
+ * store_clique()
+ *
+ * Stores a clique according to given user options.
+ *
+ * clique - the clique to store
+ * opts - storage options
+ *
+ * Returns FALSE if opts->user_function() returned FALSE; otherwise
+ * returns TRUE.
+ */
+static boolean store_clique(set_t clique, graph_t *g, clique_options *opts) {
+
+ clique_list_count++;
+
+ /* clique_list[] */
+ if (opts->clique_list) {
+ /*
+ * This has been a major source of bugs:
+ * Has clique_list_count been set to 0 before calling
+ * the recursions?
+ */
+ if (clique_list_count <= 0) {
+ fprintf(stderr,"CLIQUER INTERNAL ERROR: "
+ "clique_list_count has negative value!\n");
+ fprintf(stderr,"Please report as a bug.\n");
+ abort();
+ }
+ if (clique_list_count <= opts->clique_list_length)
+ opts->clique_list[clique_list_count-1] =
+ set_duplicate(clique);
+ }
+
+ /* user_function() */
+ if (opts->user_function) {
+ if (!opts->user_function(clique,g,opts)) {
+ /* User function requested abort. */
+ return FALSE;
+ }
+ }
+
+ return TRUE;
+}
+
+/*
+ * maximalize_clique()
+ *
+ * Adds greedily all possible vertices in g to set s to make it a maximal
+ * clique.
+ *
+ * s - clique of vertices to make maximal
+ * g - graph
+ *
+ * Note: Not very optimized (uses a simple O(n^2) routine), but is called
+ * at maximum once per clique_xxx() call, so it shouldn't matter.
+ */
+static void maximalize_clique(set_t s,graph_t *g) {
+ int i,j;
+ boolean add;
+
+ for (i=0; i < g->n; i++) {
+ add=TRUE;
+ for (j=0; j < g->n; j++) {
+ if (SET_CONTAINS_FAST(s,j) && !GRAPH_IS_EDGE(g,i,j)) {
+ add=FALSE;
+ break;
+ }
+ }
+ if (add) {
+ SET_ADD_ELEMENT(s,i);
+ }
+ }
+ return;
+}
+
+
+/*
+ * is_maximal()
+ *
+ * Check whether a clique is maximal or not.
+ *
+ * clique - set of vertices in clique
+ * g - graph
+ *
+ * Returns TRUE is clique is a maximal clique of g, otherwise FALSE.
+ */
+static boolean is_maximal(set_t clique, graph_t *g) {
+ int i,j;
+ int *table;
+ int len;
+ boolean addable;
+
+ if (temp_count) {
+ temp_count--;
+ table=temp_list[temp_count];
+ } else {
+ table=malloc(g->n * sizeof(int));
+ }
+
+ len=0;
+ for (i=0; i < g->n; i++)
+ if (SET_CONTAINS_FAST(clique,i))
+ table[len++]=i;
+
+ for (i=0; i < g->n; i++) {
+ addable=TRUE;
+ for (j=0; j<len; j++) {
+ if (!GRAPH_IS_EDGE(g,i,table[j])) {
+ addable=FALSE;
+ break;
+ }
+ }
+ if (addable) {
+ temp_list[temp_count++]=table;
+ return FALSE;
+ }
+ }
+ temp_list[temp_count++]=table;
+ return TRUE;
+}
+
+
+/*
+ * false_function()
+ *
+ * Returns FALSE. Can be used as user_function.
+ */
+static boolean false_function(set_t clique,graph_t *g,clique_options *opts) {
+ return FALSE;
+}
+
+
+
+
+/***** API-functions *****/
+
+/*
+ * clique_unweighted_max_weight()
+ *
+ * Returns the size of the maximum (sized) clique in g (or 0 if search
+ * was aborted).
+ *
+ * g - the graph
+ * opts - time printing options
+ *
+ * Note: As we don't have an algorithm faster than actually finding
+ * a maximum clique, we use clique_unweighted_find_single().
+ * This incurs only very small overhead.
+ */
+int clique_unweighted_max_weight(graph_t *g, clique_options *opts) {
+ set_t s;
+ int size;
+
+ ASSERT((sizeof(setelement)*8)==ELEMENTSIZE);
+ ASSERT(g!=NULL);
+
+ s=clique_unweighted_find_single(g,0,0,FALSE,opts);
+ if (s==NULL) {
+ /* Search was aborted. */
+ return 0;
+ }
+ size=set_size(s);
+ set_free(s);
+ return size;
+}
+
+
+/*
+ * clique_unweighted_find_single()
+ *
+ * Returns a clique with size at least min_size and at most max_size.
+ *
+ * g - the graph
+ * min_size - minimum size of clique to search for. If min_size==0,
+ * searches for maximum clique.
+ * max_size - maximum size of clique to search for. If max_size==0, no
+ * upper limit is used. If min_size==0, this 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 size/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[].
+ */
+set_t clique_unweighted_find_single(graph_t *g,int min_size,int max_size,
+ 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_size>=0);
+ ASSERT(max_size>=0);
+ ASSERT((max_size==0) || (min_size <= max_size));
+ ASSERT(!((min_size==0) && (max_size>0)));
+ ASSERT((opts->reorder_function==NULL) || (opts->reorder_map==NULL));
+
+ if ((max_size>0) && (min_size>max_size)) {
+ /* state was not changed */
+ entrance_level--;
+ return NULL;
+ }
+
+#if 0
+ if (clocks_per_sec==0)
+ clocks_per_sec=sysconf(_SC_CLK_TCK);
+ ASSERT(clocks_per_sec>0);
+#endif
+
+ /* Dynamic allocation */
+ current_clique=set_new(g->n);
+ clique_size=malloc(g->n * sizeof(int));
+ /* table allocated later */
+ temp_list=malloc((g->n+2)*sizeof(int *));
+ temp_count=0;
+
+#if 0
+ /* "start clock" */
+ gettimeofday(&realtimer,NULL);
+ times(&cputimer);
+#endif
+
+ /* reorder */
+ if (opts->reorder_function) {
+ table=opts->reorder_function(g,FALSE);
+ } 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 (unweighted_clique_search_single(table,min_size,g,opts)==0) {
+ set_free(current_clique);
+ current_clique=NULL;
+ goto cleanreturn;
+ }
+ if (maximal && (min_size>0)) {
+ maximalize_clique(current_clique,g);
+
+ if ((max_size > 0) && (set_size(current_clique) > max_size)) {
+ clique_options localopts;
+
+ s = set_new(g->n);
+ localopts.time_function = opts->time_function;
+ localopts.output = opts->output;
+ localopts.user_function = false_function;
+ localopts.clique_list = &s;
+ localopts.clique_list_length = 1;
+
+ for (i=0; i < g->n-1; i++)
+ if (clique_size[table[i]]>=min_size)
+ break;
+ if (unweighted_clique_search_all(table,i,min_size,
+ max_size,maximal,
+ g,&localopts)) {
+ set_free(current_clique);
+ current_clique=s;
+ } else {
+ set_free(current_clique);
+ current_clique=NULL;
+ }
+ }
+ }
+
+ cleanreturn:
+ s=current_clique;
+
+ /* Free resources */
+ for (i=0; i < temp_count; i++)
+ free(temp_list[i]);
+ free(temp_list);
+ free(table);
+ free(clique_size);
+
+ ENTRANCE_RESTORE();
+ entrance_level--;
+
+ return s;
+}
+
+
+/*
+ * clique_unweighted_find_all()
+ *
+ * Find all cliques with size at least min_size and at most max_size.
+ *
+ * g - the graph
+ * min_size - minimum size of cliques to search for. If min_size==0,
+ * searches for maximum cliques.
+ * max_size - maximum size of cliques to search for. If max_size==0, no
+ * upper limit is used. If min_size==0, this 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().
+ */
+int clique_unweighted_find_all(graph_t *g, int min_size, int max_size,
+ boolean maximal, clique_options *opts) {
+ int i;
+ int *table;
+ int count;
+
+ ENTRANCE_SAVE();
+ entrance_level++;
+
+ if (opts==NULL)
+ opts=clique_default_options;
+
+ ASSERT((sizeof(setelement)*8)==ELEMENTSIZE);
+ ASSERT(g!=NULL);
+ ASSERT(min_size>=0);
+ ASSERT(max_size>=0);
+ ASSERT((max_size==0) || (min_size <= max_size));
+ ASSERT(!((min_size==0) && (max_size>0)));
+ ASSERT((opts->reorder_function==NULL) || (opts->reorder_map==NULL));
+
+ if ((max_size>0) && (min_size>max_size)) {
+ /* state was not changed */
+ entrance_level--;
+ return 0;
+ }
+
+#if 0
+ if (clocks_per_sec==0)
+ clocks_per_sec=sysconf(_SC_CLK_TCK);
+ ASSERT(clocks_per_sec>0);
+#endif
+
+ /* Dynamic allocation */
+ current_clique=set_new(g->n);
+ clique_size=malloc(g->n * sizeof(int));
+ /* table allocated later */
+ temp_list=malloc((g->n+2)*sizeof(int *));
+ temp_count=0;
+
+ clique_list_count=0;
+ memset(clique_size,0,g->n * sizeof(int));
+
+#if 0
+ /* "start clock" */
+ gettimeofday(&realtimer,NULL);
+ times(&cputimer);
+#endif
+
+ /* reorder */
+ if (opts->reorder_function) {
+ table=opts->reorder_function(g,FALSE);
+ } 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));
+
+
+ /* Search as normal until there is a chance to find a suitable
+ * clique. */
+ if (unweighted_clique_search_single(table,min_size,g,opts)==0) {
+ count=0;
+ goto cleanreturn;
+ }
+
+ if (min_size==0 && max_size==0) {
+ min_size=max_size=clique_size[table[g->n-1]];
+ maximal=FALSE; /* No need to test, since we're searching
+ * for maximum cliques. */
+ }
+ if (max_size==0) {
+ max_size=INT_MAX;
+ }
+
+ for (i=0; i < g->n-1; i++)
+ if (clique_size[table[i]] >= min_size)
+ break;
+ count=unweighted_clique_search_all(table,i,min_size,max_size,
+ maximal,g,opts);
+
+ cleanreturn:
+ /* Free resources */
+ for (i=0; i<temp_count; i++)
+ free(temp_list[i]);
+ free(temp_list);
+ free(table);
+ free(clique_size);
+ set_free(current_clique);
+
+ ENTRANCE_RESTORE();
+ entrance_level--;
+
+ return count;
+}
+
+
+
+
+/*
+ * clique_max_weight()
+ *
+ * Returns the weight of the maximum weight clique in the graph (or 0 if
+ * the search was aborted).
+ *
+ * g - the graph
+ * opts - time printing options
+ *
+ * Note: As we don't have an algorithm faster than actually finding
+ * a maximum weight clique, we use clique_find_single().
+ * This incurs only very small overhead.
+ */
+int clique_max_weight(graph_t *g,clique_options *opts) {
+ set_t s;
+ int weight;
+
+ ASSERT((sizeof(setelement)*8)==ELEMENTSIZE);
+ ASSERT(g!=NULL);
+
+ s=clique_find_single(g,0,0,FALSE,opts);
+ if (s==NULL) {
+ /* Search was aborted. */
+ return 0;
+ }
+ weight=graph_subgraph_weight(g,s);
+ set_free(s);
+ return weight;
+}
+
+
+/*
+ * 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 0
+ if (clocks_per_sec==0)
+ clocks_per_sec=sysconf(_SC_CLK_TCK);
+ ASSERT(clocks_per_sec>0);
+#endif
+
+ /* 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;
+
+#if 0
+ /* "start clock" */
+ gettimeofday(&realtimer,NULL);
+ times(&cputimer);
+#endif
+
+ /* 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;
+}
+
+
+
+
+
+/*
+ * 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 0
+ if (clocks_per_sec==0)
+ clocks_per_sec=sysconf(_SC_CLK_TCK);
+ ASSERT(clocks_per_sec>0);
+#endif
+
+ 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;
+
+#if 0
+ /* "start clock" */
+ gettimeofday(&realtimer,NULL);
+ times(&cputimer);
+#endif
+
+ /* 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;
+}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+/*
+ * clique_print_time()
+ *
+ * Reports current running information every 0.1 seconds or when values
+ * change.
+ *
+ * level - re-entrance level
+ * i - current recursion level
+ * n - maximum recursion level
+ * max - weight of heaviest clique found
+ * cputime - CPU time used in algorithm so far
+ * realtime - real time used in algorithm so far
+ * opts - prints information to (FILE *)opts->output (or stdout if NULL)
+ *
+ * Returns always TRUE (ie. never requests abort).
+ */
+boolean clique_print_time(int level, int i, int n, int max,
+ double cputime, double realtime,
+ clique_options *opts) {
+ static float prev_time=100;
+ static int prev_i=100;
+ static int prev_max=100;
+ static int prev_level=0;
+ FILE *fp=opts->output;
+ int j;
+
+ if (fp==NULL)
+ fp=stdout;
+
+ if (ABS(prev_time-realtime)>0.1 || i==n || i<prev_i || max!=prev_max ||
+ level!=prev_level) {
+ for (j=1; j<level; j++)
+ fprintf(fp," ");
+ if (realtime-prev_time < 0.01 || i<=prev_i)
+ fprintf(fp,"%3d/%d (max %2d) %2.2f s "
+ "(0.00 s/round)\n",i,n,max,
+ realtime);
+ else
+ fprintf(fp,"%3d/%d (max %2d) %2.2f s "
+ "(%2.2f s/round)\n",
+ i,n,max,realtime,
+ (realtime-prev_time)/(i-prev_i));
+ prev_time=realtime;
+ prev_i=i;
+ prev_max=max;
+ prev_level=level;
+ }
+ return TRUE;
+}
+
+/*
+ * clique_print_time_always()
+ *
+ * Reports current running information.
+ *
+ * level - re-entrance level
+ * i - current recursion level
+ * n - maximum recursion level
+ * max - largest clique found
+ * cputime - CPU time used in algorithm so far
+ * realtime - real time used in algorithm so far
+ * opts - prints information to (FILE *)opts->output (or stdout if NULL)
+ *
+ * Returns always TRUE (ie. never requests abort).
+ */
+boolean clique_print_time_always(int level, int i, int n, int max,
+ double cputime, double realtime,
+ clique_options *opts) {
+ static float prev_time=100;
+ static int prev_i=100;
+ FILE *fp=opts->output;
+ int j;
+
+ if (fp==NULL)
+ fp=stdout;
+
+ for (j=1; j<level; j++)
+ fprintf(fp," ");
+
+ if (realtime-prev_time < 0.01 || i<=prev_i)
+ fprintf(fp,"%3d/%d (max %2d) %2.2f s (0.00 s/round)\n",
+ i,n,max,realtime);
+ else
+ fprintf(fp,"%3d/%d (max %2d) %2.2f s (%2.2f s/round)\n",
+ i,n,max,realtime,(realtime-prev_time)/(i-prev_i));
+ prev_time=realtime;
+ prev_i=i;
+
+ return TRUE;
+}
+
+
+/*
+ * This file contains the graph handling routines.
+ *
+ * Copyright (C) 2002 Sampo Niskanen, Patric Östergård.
+ * Licensed under the GNU GPL, read the file LICENSE for details.
+ */
+
+/*
+#include <stdio.h>
+#include <ctype.h>
+#include <string.h>
+#include "graph.h"
+*/
+
+
+/*
+ * graph_new()
+ *
+ * Returns a newly allocated graph with n vertices all with weight 1,
+ * and no edges.
+ */
+graph_t *graph_new(int n) {
+ graph_t *g;
+ int i;
+
+ ASSERT((sizeof(setelement)*8)==ELEMENTSIZE);
+ ASSERT(n>0);
+
+ g=malloc(sizeof(graph_t));
+ g->n=n;
+ g->edges=malloc(g->n * sizeof(set_t));
+ g->weights=malloc(g->n * sizeof(int));
+ for (i=0; i < g->n; i++) {
+ g->edges[i]=set_new(n);
+ g->weights[i]=1;
+ }
+ return g;
+}
+
+/*
+ * graph_free()
+ *
+ * Frees the memory associated with the graph g.
+ */
+void graph_free(graph_t *g) {
+ int i;
+
+ ASSERT((sizeof(setelement)*8)==ELEMENTSIZE);
+ ASSERT(g!=NULL);
+ ASSERT(g->n > 0);
+
+ for (i=0; i < g->n; i++) {
+ set_free(g->edges[i]);
+ }
+ free(g->weights);
+ free(g->edges);
+ free(g);
+ return;
+}
+
+
+/*
+ * graph_resize()
+ *
+ * Resizes graph g to given size. If size > g->n, the new vertices are
+ * not connected to any others and their weights are set to 1.
+ * If size < g->n, the last g->n - size vertices are removed.
+ */
+void graph_resize(graph_t *g, int size) {
+ int i;
+
+ ASSERT(g!=NULL);
+ ASSERT(g->n > 0);
+ ASSERT(size > 0);
+
+ if (g->n == size)
+ return;
+
+ /* Free/alloc extra edge-sets */
+ for (i=size; i < g->n; i++)
+ set_free(g->edges[i]);
+ g->edges=realloc(g->edges, size * sizeof(set_t));
+ for (i=g->n; i < size; i++)
+ g->edges[i]=set_new(size);
+
+ /* Resize original sets */
+ for (i=0; i < MIN(g->n,size); i++) {
+ g->edges[i]=set_resize(g->edges[i],size);
+ }
+
+ /* Weights */
+ g->weights=realloc(g->weights,size * sizeof(int));
+ for (i=g->n; i<size; i++)
+ g->weights[i]=1;
+
+ g->n=size;
+ return;
+}
+
+/*
+ * graph_crop()
+ *
+ * Resizes the graph so as to remove all highest-valued isolated vertices.
+ */
+void graph_crop(graph_t *g) {
+ int i;
+
+ for (i=g->n-1; i>=1; i--)
+ if (set_size(g->edges[i])>0)
+ break;
+ graph_resize(g,i+1);
+ return;
+}
+
+
+/*
+ * graph_weighted()
+ *
+ * Returns TRUE if all vertex weights of graph g are all the same.
+ *
+ * Note: Does NOT require weights to be 1.
+ */
+boolean graph_weighted(graph_t *g) {
+ int i,w;
+
+ w=g->weights[0];
+ for (i=1; i < g->n; i++)
+ if (g->weights[i] != w)
+ return TRUE;
+ return FALSE;
+}
+
+/*
+ * graph_edge_count()
+ *
+ * Returns the number of edges in graph g.
+ */
+int graph_edge_count(graph_t *g) {
+ int i;
+ int count=0;
+
+ for (i=0; i < g->n; i++) {
+ count += set_size(g->edges[i]);
+ }
+ return count/2;
+}
+
+/*
+ * 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;
+}
+
+
+/*
+ * 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;
+}
+
+
+/*
+ * graph_test_regular()
+ *
+ * Returns the vertex degree for regular graphs, or -1 if the graph is
+ * not regular.
+ */
+int graph_test_regular(graph_t *g) {
+ int i,n;
+
+ n=set_size(g->edges[0]);
+
+ for (i=1; i < g->n; i++) {
+ if (set_size(g->edges[i]) != n)
+ return -1;
+ }
+ return n;
+}
+
+
+
+/*
+ * This file contains the vertex reordering routines.
+ *
+ * Copyright (C) 2002 Sampo Niskanen, Patric Östergård.
+ * Licensed under the GNU GPL, read the file LICENSE for details.
+ */
+
+/*
+#include "reorder.h"
+
+#include <time.h>
+#include <sys/times.h>
+#include <stdlib.h>
+
+#include <limits.h>
+*/
+
+
+/*
+ * reorder_set()
+ *
+ * Reorders the set s with a function i -> order[i].
+ *
+ * Note: Assumes that order is the same size as SET_MAX_SIZE(s).
+ */
+void reorder_set(set_t s,int *order) {
+ set_t tmp;
+ int i,j;
+ setelement e;
+
+ ASSERT(reorder_is_bijection(order,SET_MAX_SIZE(s)));
+
+ tmp=set_new(SET_MAX_SIZE(s));
+
+ for (i=0; i<(SET_MAX_SIZE(s)/ELEMENTSIZE); i++) {
+ e=s[i];
+ if (e==0)
+ continue;
+ for (j=0; j<ELEMENTSIZE; j++) {
+ if (e&1) {
+ SET_ADD_ELEMENT(tmp,order[i*ELEMENTSIZE+j]);
+ }
+ e = e>>1;
+ }
+ }
+ if (SET_MAX_SIZE(s)%ELEMENTSIZE) {
+ e=s[i];
+ for (j=0; j<(SET_MAX_SIZE(s)%ELEMENTSIZE); j++) {
+ if (e&1) {
+ SET_ADD_ELEMENT(tmp,order[i*ELEMENTSIZE+j]);
+ }
+ e = e>>1;
+ }
+ }
+ set_copy(s,tmp);
+ set_free(tmp);
+ return;
+}
+
+
+/*
+ * reorder_graph()
+ *
+ * Reorders the vertices in the graph with function i -> order[i].
+ *
+ * Note: Assumes that order is of size g->n.
+ */
+void reorder_graph(graph_t *g, int *order) {
+ int i;
+ set_t *tmp_e;
+ int *tmp_w;
+
+ ASSERT(reorder_is_bijection(order,g->n));
+
+ tmp_e=malloc(g->n * sizeof(set_t));
+ tmp_w=malloc(g->n * sizeof(int));
+ for (i=0; i<g->n; i++) {
+ reorder_set(g->edges[i],order);
+ tmp_e[order[i]]=g->edges[i];
+ tmp_w[order[i]]=g->weights[i];
+ }
+ for (i=0; i<g->n; i++) {
+ g->edges[i]=tmp_e[i];
+ g->weights[i]=tmp_w[i];
+ }
+ free(tmp_e);
+ free(tmp_w);
+ return;
+}
+
+
+
+/*
+ * reorder_duplicate()
+ *
+ * Returns a newly allocated duplicate of the given ordering.
+ */
+int *reorder_duplicate(int *order,int n) {
+ int *new;
+
+ new=malloc(n*sizeof(int));
+ memcpy(new,order,n*sizeof(int));
+ return new;
+}
+
+/*
+ * reorder_invert()
+ *
+ * Inverts the given ordering so that new[old[i]]==i.
+ *
+ * Note: Asserts that order is a bijection.
+ */
+void reorder_invert(int *order,int n) {
+ int *new;
+ int i;
+
+ ASSERT(reorder_is_bijection(order,n));
+
+ new=malloc(n*sizeof(int));
+ for (i=0; i<n; i++)
+ new[order[i]]=i;
+ for (i=0; i<n; i++)
+ order[i]=new[i];
+ free(new);
+ return;
+}
+
+/*
+ * reorder_reverse()
+ *
+ * Reverses the given ordering so that new[i] == n-1 - old[i].
+ */
+void reorder_reverse(int *order,int n) {
+ int i;
+
+ for (i=0; i<n; i++)
+ order[i] = n-1 - order[i];
+ return;
+}
+
+/*
+ * reorder_is_bijection
+ *
+ * Checks that an ordering is a bijection {0,...,n-1} -> {0,...,n-1}.
+ *
+ * Returns TRUE if it is a bijection, FALSE otherwise.
+ */
+boolean reorder_is_bijection(int *order,int n) {
+ boolean *used;
+ int i;
+
+ used=calloc(n,sizeof(boolean));
+ for (i=0; i<n; i++) {
+ if (order[i]<0 || order[i]>=n) {
+ free(used);
+ return FALSE;
+ }
+ if (used[order[i]]) {
+ free(used);
+ return FALSE;
+ }
+ used[order[i]]=TRUE;
+ }
+ for (i=0; i<n; i++) {
+ if (!used[i]) {
+ free(used);
+ return FALSE;
+ }
+ }
+ free(used);
+ return TRUE;
+}
+
+/*
+ * reorder_ident()
+ *
+ * Returns a newly allocated identity ordering of size n, ie. order[i]==i.
+ */
+int *reorder_ident(int n) {
+ int i;
+ int *order;
+
+ order=malloc(n*sizeof(int));
+ for (i=0; i<n; i++)
+ order[i]=i;
+ return order;
+}
+
+
+
+/*** Reordering functions for use in clique_options ***/
+
+/*
+ * reorder_by_ident()
+ *
+ * Returns an identity ordering.
+ */
+int *reorder_by_ident(graph_t *g,boolean weighted) {
+ return reorder_ident(g->n);
+}
+
+/*
+ * reorder_by_reverse()
+ *
+ * Returns a reverse identity ordering.
+ */
+int *reorder_by_reverse(graph_t *g,boolean weighted) {
+ int i;
+ int *order;
+
+ order=malloc(g->n * sizeof(int));
+ for (i=0; i < g->n; i++)
+ order[i]=g->n-i-1;
+ return order;
+}
+
+/*
+ * reorder_by_greedy_coloring()
+ *
+ * Equivalent to reorder_by_weighted_greedy_coloring or
+ * reorder_by_unweighted_greedy_coloring according to the value of weighted.
+ */
+int *reorder_by_greedy_coloring(graph_t *g,boolean weighted) {
+ if (weighted)
+ return reorder_by_weighted_greedy_coloring(g,weighted);
+ else
+ return reorder_by_unweighted_greedy_coloring(g,weighted);
+}
+
+
+/*
+ * reorder_by_unweighted_greedy_coloring()
+ *
+ * Returns an ordering for the graph g by coloring the clique one
+ * color at a time, always adding the vertex of largest degree within
+ * the uncolored graph, and numbering these vertices 0, 1, ...
+ *
+ * Experimentally efficient for use with unweighted graphs.
+ */
+int *reorder_by_unweighted_greedy_coloring(graph_t *g,boolean weighted) {
+ int i,j,v;
+ boolean *tmp_used;
+ int *degree; /* -1 for used vertices */
+ int *order;
+ int maxdegree,maxvertex=0;
+ boolean samecolor;
+
+ tmp_used=calloc(g->n,sizeof(boolean));
+ degree=calloc(g->n,sizeof(int));
+ order=calloc(g->n,sizeof(int));
+
+ for (i=0; i < g->n; i++) {
+ for (j=0; j < g->n; j++) {
+ ASSERT(!((i==j) && GRAPH_IS_EDGE(g,i,j)));
+ if (GRAPH_IS_EDGE(g,i,j))
+ degree[i]++;
+ }
+ }
+
+ v=0;
+ while (v < g->n) {
+ /* Reset tmp_used. */
+ memset(tmp_used,0,g->n * sizeof(boolean));
+
+ do {
+ /* Find vertex to be colored. */
+ maxdegree=0;
+ samecolor=FALSE;
+ for (i=0; i < g->n; i++) {
+ if (!tmp_used[i] && degree[i] >= maxdegree) {
+ maxvertex=i;
+ maxdegree=degree[i];
+ samecolor=TRUE;
+ }
+ }
+ if (samecolor) {
+ order[v]=maxvertex;
+ degree[maxvertex]=-1;
+ v++;
+
+ /* Mark neighbors not to color with same
+ * color and update neighbor degrees. */
+ for (i=0; i < g->n; i++) {
+ if (GRAPH_IS_EDGE(g,maxvertex,i)) {
+ tmp_used[i]=TRUE;
+ degree[i]--;
+ }
+ }
+ }
+ } while (samecolor);
+ }
+
+ free(tmp_used);
+ free(degree);
+ return order;
+}
+
+/*
+ * reorder_by_weighted_greedy_coloring()
+ *
+ * Returns an ordering for the graph g by coloring the clique one
+ * color at a time, always adding the vertex that (in order of importance):
+ * 1. has the minimum weight in the remaining graph
+ * 2. has the largest sum of weights surrounding the vertex
+ *
+ * Experimentally efficient for use with weighted graphs.
+ */
+int *reorder_by_weighted_greedy_coloring(graph_t *g, boolean weighted) {
+ int i,j,p=0;
+ int cnt;
+ int *nwt; /* Sum of surrounding vertices' weights */
+ int min_wt,max_nwt;
+ boolean *used;
+ int *order;
+
+ nwt=malloc(g->n * sizeof(int));
+ order=malloc(g->n * sizeof(int));
+ used=calloc(g->n,sizeof(boolean));
+
+ for (i=0; i < g->n; i++) {
+ nwt[i]=0;
+ for (j=0; j < g->n; j++)
+ if (GRAPH_IS_EDGE(g, i, j))
+ nwt[i] += g->weights[j];
+ }
+
+ for (cnt=0; cnt < g->n; cnt++) {
+ min_wt=INT_MAX;
+ max_nwt=-1;
+ for (i=g->n-1; i>=0; i--)
+ if ((!used[i]) && (g->weights[i] < min_wt))
+ min_wt=g->weights[i];
+ for (i=g->n-1; i>=0; i--) {
+ if (used[i] || (g->weights[i] > min_wt))
+ continue;
+ if (nwt[i] > max_nwt) {
+ max_nwt=nwt[i];
+ p=i;
+ }
+ }
+ order[cnt]=p;
+ used[p]=TRUE;
+ for (j=0; j < g->n; j++)
+ if ((!used[j]) && (GRAPH_IS_EDGE(g, p, j)))
+ nwt[j] -= g->weights[p];
+ }
+
+ free(nwt);
+ free(used);
+
+ ASSERT(reorder_is_bijection(order,g->n));
+
+ return order;
+}
+
+/*
+ * reorder_by_degree()
+ *
+ * Returns a reordering of the graph g so that the vertices with largest
+ * degrees (most neighbors) are first.
+ */
+int *reorder_by_degree(graph_t *g, boolean weighted) {
+ int i,j,v;
+ int *degree;
+ int *order;
+ int maxdegree,maxvertex=0;
+
+ degree=calloc(g->n,sizeof(int));
+ order=calloc(g->n,sizeof(int));
+
+ for (i=0; i < g->n; i++) {
+ for (j=0; j < g->n; j++) {
+ ASSERT(!((i==j) && GRAPH_IS_EDGE(g,i,j)));
+ if (GRAPH_IS_EDGE(g,i,j))
+ degree[i]++;
+ }
+ }
+
+ for (v=0; v < g->n; v++) {
+ maxdegree=0;
+ for (i=0; i < g->n; i++) {
+ if (degree[i] >= maxdegree) {
+ maxvertex=i;
+ maxdegree=degree[i];
+ }
+ }
+ order[v]=maxvertex;
+ degree[maxvertex]=-1; /* used */
+/*** Max. degree withing unselected graph:
+ for (i=0; i < g->n; i++) {
+ if (GRAPH_IS_EDGE(g,maxvertex,i))
+ degree[i]--;
+ }
+***/
+ }
+
+ free(degree);
+ return order;
+}
+
+/*
+ * reorder_by_random()
+ *
+ * Returns a random reordering for graph g.
+ * Note: Used the functions rand() and srand() to generate the random
+ * numbers. srand() is re-initialized every time reorder_by_random()
+ * is called using the system time.
+ */
+int *reorder_by_random(graph_t *g, boolean weighted) {
+ /* struct tms t; */
+ int i,r;
+ int *new;
+ boolean *used;
+
+/*
+ srand(times(&t)+time(NULL));
+*/
+ INITRANBYTIME;
+
+ new=calloc(g->n, sizeof(int));
+ used=calloc(g->n, sizeof(boolean));
+ for (i=0; i < g->n; i++) {
+ do {
+ r=NEXTRAN % g->n;
+ } while (used[r]);
+ new[i]=r;
+ used[r]=TRUE;
+ }
+ free(used);
+ return new;
+}
+
+/************************************************************************/
+
+/* This is an interface between nauty and cliquer for finding
+ cliques of a given size in an undirected graph. */
+
+int
+find_clique(graph *g, int m, int n, int min, int max, boolean maximal)
+/* If there is a clique of size [min,max], perhaps required to be
+ maximal, then return its size. If there is none, return 0.
+ It is required that min <= max. Use min=max=0 to ask for
+ maximum cliques. */
+{
+ graph_t *gg;
+ set_t cliq;
+ set *gi;
+ int i,j,size;
+
+ gg = graph_new(n);
+
+ for (i = 0, gi = g; i < n; ++i, gi += m)
+ for (j = i; (j = nextelement(gi,m,j)) >= 0; )
+ GRAPH_ADD_EDGE(gg,i,j);
+
+ cliq = clique_unweighted_find_single(gg,min,max,maximal,NULL);
+
+ if (cliq)
+ {
+ size = set_size(cliq);
+ set_free(cliq);
+ }
+ else
+ size = 0;
+
+ graph_free(gg);
+
+ return size;
+}
+
+
+int
+find_indset(graph *g, int m, int n, int min, int max, boolean maximal)
+/* If there is an independent set of size [min,max], perhaps required
+ to be maximal, then return its size. If there is none, return 0.
+ It is required that min <= max. Use min=max=0 to ask for
+ maximum independent sets. */
+{
+ graph_t *gg;
+ set_t cliq;
+ set *gi;
+ int i,j,jj,size;
+
+ gg = graph_new(n);
+
+ /* Make gg the complement of g */
+ for (i = 0, gi = g; i < n; ++i, gi += m)
+ {
+ for (j = jj = i; (j = nextelement(gi,m,j)) >= 0; )
+ {
+ while (++jj < j) GRAPH_ADD_EDGE(gg,i,jj);
+ }
+ while (++jj < n) GRAPH_ADD_EDGE(gg,i,jj);
+ }
+
+ cliq = clique_unweighted_find_single(gg,min,max,maximal,NULL);
+
+ if (cliq)
+ {
+ size = set_size(cliq);
+ set_free(cliq);
+ }
+ else
+ size = 0;
+
+ graph_free(gg);
+
+ return size;
+}
+