diff --git a/doc/Makefile.am b/doc/Makefile.am
index 0d1090ef1..6cce03244 100644
--- a/doc/Makefile.am
+++ b/doc/Makefile.am
@@ -156,7 +156,7 @@ community.xml: community.xxml $(SRCDIR)/community.c $(SRCDIR)/clustertool.cpp $(
$(DOXROX) -t $< -e $(REGEX) -o $@ $(SRCDIR)/community.c $(SRCDIR)/clustertool.cpp $(SRCDIR)/walktrap.cpp $(SRCDIR)/fast_community.c $(SRCDIR)/optimal_modularity.c $(INCLUDEDIR)/igraph_community.h $(SRCDIR)/infomap.cc
cliques.xml: cliques.xxml $(SRCDIR)/cliques.c $(SRCDIR)/maximal_cliques.c
- $(DOXROX) -t $< -e $(REGEX) -o $@ $(SRCDIR)/cliques.c $(SRCDIR)/maximal_cliques.c
+ $(DOXROX) -t $< -e $(REGEX) -o $@ $(SRCDIR)/cliques.c $(SRCDIR)/maximal_cliques.c $(INCLUDEDIR)/igraph_cliques.h
stack.xml: stack.xxml $(SRCDIR)/stack.pmt
$(DOXROX) -t $< -e $(REGEX) -o $@ $(SRCDIR)/stack.pmt
@@ -272,4 +272,4 @@ igraph-docs.pdf: igraph-docs.xml $(DOCFIX2)
&& fop -fo igraph-docs.fo -pdf igraph-docs.pdf
CLEANFILES=$(DOCINCLUDES) html/*.html html/stamp \
- igraph-docs.{dvi,info,pdf,ps,texi,txml} igraph-docs2.xml
+ igraph-docs.{dvi,info,pdf,ps,texi,txml} igraph-docs2.xml
diff --git a/doc/cliques.xxml b/doc/cliques.xxml
index 60c12b15d..ad1640522 100644
--- a/doc/cliques.xxml
+++ b/doc/cliques.xxml
@@ -1,4 +1,4 @@
-
]>
@@ -13,12 +13,21 @@ to cliques and independent vertex sets.
+
+
Independent Vertex Sets
diff --git a/examples/benchmarks/bench.h b/examples/benchmarks/bench.h
index 889c6ab62..328a783eb 100644
--- a/examples/benchmarks/bench.h
+++ b/examples/benchmarks/bench.h
@@ -24,7 +24,7 @@
#ifndef IGRAPH_BENCH_H
#define IGRAPH_BENCH_H
-inline void igraph_get_cpu_time(igraph_real_t *data) {
+static inline void igraph_get_cpu_time(igraph_real_t *data) {
struct rusage self, children;
getrusage(RUSAGE_SELF, &self);
diff --git a/examples/benchmarks/igraph_cliques.c b/examples/benchmarks/igraph_cliques.c
new file mode 100644
index 000000000..6a28c3860
--- /dev/null
+++ b/examples/benchmarks/igraph_cliques.c
@@ -0,0 +1,32 @@
+
+#include
+
+#include "bench.h"
+
+int main() {
+ igraph_t g;
+ igraph_vector_ptr_t res;
+ igraph_integer_t i, n;
+
+ igraph_rng_seed(igraph_rng_default(), 42);
+
+ igraph_erdos_renyi_game(&g, IGRAPH_ERDOS_RENYI_GNM, 100, 3000, /* directed = */ 0, /* loops= */ 0);
+
+ igraph_vector_ptr_init(&res, 0);
+
+ BENCH("1 Cliques in random graph with 100 vertices and 3000 edges",
+ igraph_cliques(&g, &res, /* min_size= */ 0, /* max_size= */ 0);
+ );
+
+ igraph_destroy(&g);
+
+ n=igraph_vector_ptr_size(&res);
+ for (i=0; i
#include
+int compare_vectors(const void *p1, const void *p2) {
+ igraph_vector_t *v1, *v2;
+ long s1, s2, i;
+
+ v1 = *((igraph_vector_t **) p1);
+ v2 = *((igraph_vector_t **) p2);
+ s1 = igraph_vector_size(v1);
+ s2 = igraph_vector_size(v2);
+ if (s1 < s2)
+ return -1;
+ if (s1 > s2)
+ return 1;
+ for (i=0; i < s1; ++i) {
+ if (VECTOR(*v1)[i] < VECTOR(*v2)[i])
+ return -1;
+ if (VECTOR(*v1)[i] > VECTOR(*v2)[i])
+ return 1;
+ }
+ return 0;
+}
+
+/* Takes a pointer vector of vectors. Sorts each vector, then sorts the pointer vector */
+void canonicalize_list(igraph_vector_ptr_t *list) {
+ long i, len;
+ len = igraph_vector_ptr_size(list);
+ for (i=0; ilist))[ud->i])) != 0) {
+ printf("igraph_cliques() and igraph_cliques_callback() give different results.\n");
+ cont = 0; /* false */
+ }
+
+ igraph_vector_destroy(clique);
+ igraph_free(clique);
+
+ ud->i += 1;
+
+ return cont;
+}
+
+void test_callback(const igraph_t *graph) {
+ igraph_vector_ptr_t list;
+ struct userdata ud;
+
+ igraph_vector_ptr_init(&list, 0);
+ igraph_cliques(graph, &list, 0, 0);
+
+ ud.i = 0;
+ ud.list = &list;
+
+ igraph_cliques_callback(graph, 0, 0, &handler, (void *) &ud);
+}
+
+
int main() {
igraph_t g;
@@ -60,10 +131,10 @@ int main() {
}
n = igraph_vector_ptr_size(&result);
printf("%ld cliques found\n", (long)n);
+ canonicalize_list(&result);
for (i=0; i
+#include
+
+int compare_vectors(const void *p1, const void *p2) {
+ igraph_vector_t *v1, *v2;
+ long s1, s2, i;
+
+ v1 = *((igraph_vector_t **) p1);
+ v2 = *((igraph_vector_t **) p2);
+ s1 = igraph_vector_size(v1);
+ s2 = igraph_vector_size(v2);
+ if (s1 < s2)
+ return -1;
+ if (s1 > s2)
+ return 1;
+ for (i=0; i < s1; ++i) {
+ if (VECTOR(*v1)[i] < VECTOR(*v2)[i])
+ return -1;
+ if (VECTOR(*v1)[i] > VECTOR(*v2)[i])
+ return 1;
+ }
+ return 0;
+}
+
+/* Takes a pointer vector of vectors. Sorts each vector, then sorts the pointer vector */
+void canonicalize_list(igraph_vector_ptr_t *list) {
+ long i, len;
+ len = igraph_vector_ptr_size(list);
+ for (i=0; i.
+
+
+License
+
+Cliquer is Copyright (C) 2002 Sampo Niskanen, Patric Ostergard.
+
+Cliquer is licensed under the GNU General Public License as published
+by the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version. The full license is included in
+the file LICENSE.
+
+Basically, you can use Cliquer for any purpose, provided that any
+programs or modifications you make and distribute are also licensed
+under the GNU GPL.
+
+ABSOLUTELY NO GUARANTEES OR WARRANTIES are made concerning the
+suitability, correctness, or any other aspect of these routines.
+
+
+Contact
+
+Cliquer was mainly written by Sampo Niskanen .
+
+For bug-fixes, feedback, and, in particular, for putting your
+name on the mailing list for important information regarding Cliquer,
+please contact:
+
+Patric Ostergard
+Department of Communications and Networking
+Aalto University
+P.O. Box 13000, 00076 Aalto
+FINLAND
+
diff --git a/src/cliquer/cliquer.c b/src/cliquer/cliquer.c
new file mode 100644
index 000000000..63b7efb11
--- /dev/null
+++ b/src/cliquer/cliquer.c
@@ -0,0 +1,1770 @@
+
+/*
+ * 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.
+ */
+
+#include
+#include
+#include
+/*
+#include
+#include
+#include
+*/
+
+#include "cliquer.h"
+
+#include "config.h"
+
+
+/* Default cliquer options */
+IGRAPH_THREAD_LOCAL static clique_options clique_default_options_struct = {
+ reorder_by_default, NULL, /*clique_print_time*/ NULL, NULL, NULL, NULL, NULL, 0
+};
+IGRAPH_THREAD_LOCAL 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. */
+IGRAPH_THREAD_LOCAL static int *clique_size; /* c[i] == max. clique size in {0,1,...,i-1} */
+IGRAPH_THREAD_LOCAL static set_t current_clique; /* Current clique being searched. */
+IGRAPH_THREAD_LOCAL static set_t best_clique; /* Largest/heaviest clique found so far. */
+/*static struct tms cputimer;*/ /* Timer for opts->time_function() */
+/*static struct timeval realtimer;*/ /* Timer for opts->time_function() */
+IGRAPH_THREAD_LOCAL static int clique_list_count=0; /* No. of cliques in opts->clique_list[] */
+IGRAPH_THREAD_LOCAL static int weight_multiplier=1; /* Weights multiplied by this when passing
+ * to time_function(). */
+
+/* List cache (contains memory blocks of size g->n * sizeof(int)) */
+IGRAPH_THREAD_LOCAL static int **temp_list=NULL;
+IGRAPH_THREAD_LOCAL static 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.
+ */
+IGRAPH_THREAD_LOCAL 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));*/
+
+
+/* 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.
+ *
+ * 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) {
+ /*
+ struct tms tms;
+ struct timeval timeval;
+ */
+ 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; jtime_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;
+ }
+ }
+ */
+
+ 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) {
+ /*
+ struct timeval timeval;
+ struct tms tms;
+ */
+ 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; jtime_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) {
+ /*
+ struct timeval timeval;
+ struct tms tms;
+ */
+ 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; jweights[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 (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;
+ }
+ }
+ */
+ }
+ 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) {
+ /*
+ struct timeval timeval;
+ struct tms tms;
+ */
+ 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; jweights[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 (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;
+ }
+ }
+ */
+ }
+ 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; jtime_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 (clocks_per_sec==0)
+ clocks_per_sec=sysconf(_SC_CLK_TCK);
+ ASSERT(clocks_per_sec>0);
+ */
+
+ /* 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;
+
+ /* "start clock" */
+ /*
+ gettimeofday(&realtimer,NULL);
+ times(&cputimer);
+ */
+
+ /* 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 (clocks_per_sec==0)
+ clocks_per_sec=sysconf(_SC_CLK_TCK);
+ ASSERT(clocks_per_sec>0);
+ */
+
+ /* 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));
+
+ /* "start clock" */
+ /*
+ gettimeofday(&realtimer,NULL);
+ times(&cputimer);
+ */
+
+ /* 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; itime_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;
+}
+
+
+
+
+
+/*
+ * 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;
+}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+#if 0
+/*
+ * 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 || ioutput (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
+
+#include "set.h"
+#include "graph.h"
+#include "reorder.h"
+
+typedef struct _clique_options clique_options;
+struct _clique_options {
+ int *(*reorder_function)(graph_t *, boolean);
+ int *reorder_map;
+
+ /* arguments: level, n, max, user_time, system_time, opts */
+ boolean (*time_function)(int,int,int,int,double,double,
+ clique_options *);
+ FILE *output;
+
+ boolean (*user_function)(set_t,graph_t *,clique_options *);
+ void *user_data;
+ set_t *clique_list;
+ int clique_list_length;
+};
+
+extern clique_options *clique_default_options;
+
+/* Weighted clique functions */
+extern int clique_max_weight(graph_t *g,clique_options *opts);
+extern set_t clique_find_single(graph_t *g,int min_weight,int max_weight,
+ boolean maximal, clique_options *opts);
+extern int clique_find_all(graph_t *g, int req_weight, boolean exact,
+ boolean maximal, clique_options *opts);
+
+/* Unweighted clique functions */
+#define clique_unweighted_max_size clique_unweighted_max_weight
+extern int clique_unweighted_max_weight(graph_t *g, clique_options *opts);
+extern set_t clique_unweighted_find_single(graph_t *g,int min_size,
+ int max_size,boolean maximal,
+ clique_options *opts);
+extern int clique_unweighted_find_all(graph_t *g, int min_size, int max_size,
+ boolean maximal, clique_options *opts);
+
+/* Time printing functions */
+/*
+extern boolean clique_print_time(int level, int i, int n, int max,
+ double cputime, double realtime,
+ clique_options *opts);
+extern boolean clique_print_time_always(int level, int i, int n, int max,
+ double cputime, double realtime,
+ clique_options *opts);
+*/
+
+/* Alternate spelling (let's be a little forgiving): */
+#define cliquer_options clique_options
+#define cliquer_default_options clique_default_options
+
+#endif /* !CLIQUER_H */
diff --git a/src/cliquer/cliquerconf.h b/src/cliquer/cliquerconf.h
new file mode 100644
index 000000000..8b4d176e6
--- /dev/null
+++ b/src/cliquer/cliquerconf.h
@@ -0,0 +1,68 @@
+
+#ifndef CLIQUERCONF_H
+#define CLIQUERCONF_H
+
+/*
+ * setelement is the basic memory type used in sets. It is often fastest
+ * to be as large as can fit into the CPU registers.
+ *
+ * ELEMENTSIZE is the size of one setelement, measured in bits. It must
+ * be either 16, 32 or 64 (otherwise additional changes must be made to
+ * the source).
+ *
+ * The default is to use "unsigned long int" and attempt to guess the
+ * size using , which should work pretty well. Check functioning
+ * with "make test".
+ */
+
+/* typedef unsigned long int setelement; */
+/* #define ELEMENTSIZE 64 */
+
+
+/*
+ * INLINE is a command prepended to function declarations to instruct the
+ * compiler to inline the function. If inlining is not desired, define blank.
+ *
+ * The default is to use "inline", which is recognized by most compilers.
+ */
+
+/* #define INLINE */
+/* #define INLINE __inline__ */
+#if __STDC_VERSION__ >= 199901L
+ #define INLINE inline
+#else
+ #if defined(_MSC_VER)
+ #define INLINE __inline
+ #elseif defined(__GNUC__)
+ #define INLINE __inline__
+ #else
+ #define INLINE
+ #endif
+#endif
+
+
+/*
+ * Set handling functions are defined as static functions in set.h for
+ * performance reasons. This may cause unnecessary warnings from the
+ * compiler. Some compilers (such as GCC) have the possibility to turn
+ * off the warnings on a per-function basis using a flag prepended to
+ * the function declaration.
+ *
+ * The default is to use the correct attribute when compiling with GCC,
+ * or no flag otherwise.
+ */
+
+/* #define UNUSED_FUNCTION __attribute__((unused)) */
+/* #define UNUSED_FUNCTION */
+
+
+/*
+ * Uncommenting the following will disable all assertions (checks that
+ * function arguments and other variables are correct). This is highly
+ * discouraged, as it allows bugs to go unnoticed easier. The assertions
+ * are set so that they do not slow down programs notably.
+ */
+
+/* #define ASSERT(x) */
+
+#endif /* !CLIQUERCONF_H */
diff --git a/src/cliquer/graph.c b/src/cliquer/graph.c
new file mode 100644
index 000000000..76dcc38db
--- /dev/null
+++ b/src/cliquer/graph.c
@@ -0,0 +1,763 @@
+
+/*
+ * 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
+#include
+#include
+#include "graph.h"
+
+
+/*
+static graph_t *graph_read_dimacs_binary(FILE *fp,char *firstline);
+static graph_t *graph_read_dimacs_ascii(FILE *fp,char *firstline);
+*/
+
+
+/*
+ * 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; iweights[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;
+}
+
+
+#if 0
+/*
+ * graph_write_dimacs_ascii_file()
+ *
+ * Writes an ASCII dimacs-format file of graph g, with comment, to
+ * given file.
+ *
+ * Returns TRUE if successful, FALSE if an error occurred.
+ */
+boolean graph_write_dimacs_ascii_file(graph_t *g, char *comment, char *file) {
+ FILE *fp;
+
+ ASSERT((sizeof(setelement)*8)==ELEMENTSIZE);
+ ASSERT(file!=NULL);
+
+ if ((fp=fopen(file,"wb"))==NULL)
+ return FALSE;
+ if (!graph_write_dimacs_ascii(g,comment,fp)) {
+ fclose(fp);
+ return FALSE;
+ }
+ fclose(fp);
+ return TRUE;
+}
+
+/*
+ * graph_write_dimacs_ascii()
+ *
+ * Writes an ASCII dimacs-format file of graph g, with comment, to the
+ * file stream fp.
+ *
+ * Returns TRUE if successful, FALSE if an error occurred.
+ */
+boolean graph_write_dimacs_ascii(graph_t *g, char *comment, FILE *fp) {
+ int i,j;
+
+ ASSERT((sizeof(setelement)*8)==ELEMENTSIZE);
+ ASSERT(graph_test(g,NULL));
+ ASSERT(fp!=NULL);
+
+ if (comment)
+ fprintf(fp,"c %s\n",comment);
+ fprintf(fp,"p edge %d %d\n",g->n,graph_edge_count(g));
+ for (i=0; i < g->n; i++)
+ if (g->weights[i]!=1)
+ fprintf(fp,"n %d %d\n",i+1,g->weights[i]);
+ for (i=0; i < g->n; i++)
+ for (j=0; j= headersize) { \
+ headersize+=1024; \
+ header=realloc(header,headersize); \
+} \
+strncat(header,s,1000); \
+headerlength+=strlen(s);
+
+boolean graph_write_dimacs_binary(graph_t *g, char *comment,FILE *fp) {
+ char *buf;
+ char *header=NULL;
+ int headersize=0;
+ int headerlength=0;
+ int i,j;
+
+ ASSERT((sizeof(setelement)*8)==ELEMENTSIZE);
+ ASSERT(graph_test(g,NULL));
+ ASSERT(fp!=NULL);
+
+ buf=malloc(MAX(1024,g->n/8+1));
+ header=malloc(1024);
+ header[0]=0;
+ headersize=1024;
+ if (comment) {
+ strcpy(buf,"c ");
+ strncat(buf,comment,1000);
+ strcat(buf,"\n");
+ STR_APPEND(buf);
+ }
+ sprintf(buf,"p edge %d %d\n",g->n,graph_edge_count(g));
+ STR_APPEND(buf);
+ for (i=0; i < g->n; i++) {
+ if (g->weights[i]!=1) {
+ sprintf(buf,"n %d %d\n",i+1,g->weights[i]);
+ STR_APPEND(buf);
+ }
+ }
+
+ fprintf(fp,"%d\n",(int)strlen(header));
+ fprintf(fp,"%s",header);
+ free(header);
+
+ for (i=0; i < g->n; i++) {
+ memset(buf,0,i/8+1);
+ for (j=0; j=strlen(str)) /* blank line */
+ return TRUE;
+ if (str[i+1]!=0 && !isspace(str[i+1])) /* not 1-char field */
+ return FALSE;
+
+ switch (str[i]) {
+ case 'c':
+ return TRUE;
+ case 'p':
+ if (g->n != 0)
+ return FALSE;
+ if (sscanf(str," p %15s %d %d %2s",tmp,&(g->n),&i,tmp)!=3)
+ return FALSE;
+ if (g->n <= 0)
+ return FALSE;
+ g->edges=calloc(g->n,sizeof(set_t));
+ for (i=0; in; i++)
+ g->edges[i]=set_new(g->n);
+ g->weights=calloc(g->n,sizeof(int));
+ for (i=0; in; i++)
+ g->weights[i]=1;
+ return TRUE;
+ case 'n':
+ if ((g->n <= 0) || (g->weights == NULL))
+ return FALSE;
+ if (sscanf(str," n %d %d %2s",&i,&w,tmp)!=2)
+ return FALSE;
+ if (i<1 || i>g->n)
+ return FALSE;
+ if (w<=0)
+ return FALSE;
+ g->weights[i-1]=w;
+ return TRUE;
+ case 'e':
+ if ((g->n <= 0) || (g->edges == NULL))
+ return FALSE;
+ if (sscanf(str," e %d %d %2s",&i,&j,tmp)!=2)
+ return FALSE;
+ if (i<1 || j<1 || i>g->n || j>g->n)
+ return FALSE;
+ if (i==j) /* We want antireflexive graphs. */
+ return TRUE;
+ GRAPH_ADD_EDGE(g,i-1,j-1);
+ return TRUE;
+ case 'd':
+ case 'v':
+ case 'x':
+ return TRUE;
+ default:
+ fprintf(stderr,"Warning: ignoring field '%c' in "
+ "input.\n",str[i]);
+ return TRUE;
+ }
+}
+
+
+/*
+ * graph_read_dimacs_binary()
+ *
+ * Reads a dimacs-format binary file from file stream fp with the first
+ * line being firstline.
+ *
+ * Returns the newly-allocated graph or NULL if an error occurred.
+ *
+ * TODO: This function leaks memory when reading erroneous files.
+ */
+static graph_t *graph_read_dimacs_binary(FILE *fp,char *firstline) {
+ int length=0;
+ graph_t *g;
+ int i,j;
+ char *buffer;
+ char *start;
+ char *end;
+ char **buf;
+ char tmp[10];
+
+ if (sscanf(firstline," %d %2s",&length,tmp)!=1)
+ return NULL;
+ if (length<=0) {
+ fprintf(stderr,"Malformed preamble: preamble size < 0.\n");
+ return NULL;
+ }
+ buffer=malloc(length+2);
+ if (fread(buffer,1,length,fp)n <= 0) {
+ fprintf(stderr,"Malformed preamble: number of "
+ "vertices <= 0\n");
+ free(g);
+ return NULL;
+ }
+
+ /* Binary part. */
+ buf=calloc(g->n,sizeof(char*));
+ for (i=0; i < g->n; i++) {
+ buf[i]=calloc(g->n,1);
+ if (fread(buf[i],1,i/8+1,fp) < (i/8+1)) {
+ fprintf(stderr,"Unexpected end of file when "
+ "reading graph.\n");
+ return NULL;
+ }
+ }
+
+ for (i=0; i < g->n; i++) {
+ for (j=0; jn <= 0) {
+ free(g);
+ fprintf(stderr,"Unexpected end of file when reading graph.\n");
+ return NULL;
+ }
+
+ return g;
+}
+#endif
+
+
+/*
+ * 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 (weightweights[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)
+ 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;
+}
+
diff --git a/src/cliquer/graph.h b/src/cliquer/graph.h
new file mode 100644
index 000000000..956f2a1d9
--- /dev/null
+++ b/src/cliquer/graph.h
@@ -0,0 +1,75 @@
+
+#ifndef CLIQUER_GRAPH_H
+#define CLIQUER_GRAPH_H
+
+#include "set.h"
+
+typedef struct _graph_t graph_t;
+struct _graph_t {
+ int n; /* Vertices numbered 0...n-1 */
+ set_t *edges; /* A list of n sets (the edges). */
+ int *weights; /* A list of n vertex weights. */
+};
+
+
+#define GRAPH_IS_EDGE_FAST(g,i,j) (SET_CONTAINS_FAST((g)->edges[(i)],(j)))
+#define GRAPH_IS_EDGE(g,i,j) (((i)<((g)->n))?SET_CONTAINS((g)->edges[(i)], \
+ (j)):FALSE)
+#define GRAPH_ADD_EDGE(g,i,j) do { \
+ SET_ADD_ELEMENT((g)->edges[(i)],(j)); \
+ SET_ADD_ELEMENT((g)->edges[(j)],(i)); \
+} while (FALSE)
+#define GRAPH_DEL_EDGE(g,i,j) do { \
+ SET_DEL_ELEMENT((g)->edges[(i)],(j)); \
+ SET_DEL_ELEMENT((g)->edges[(j)],(i)); \
+} while (FALSE)
+
+
+extern graph_t *graph_new(int n);
+extern void graph_free(graph_t *g);
+extern void graph_resize(graph_t *g, int size);
+extern void graph_crop(graph_t *g);
+
+extern boolean graph_weighted(graph_t *g);
+extern int graph_edge_count(graph_t *g);
+
+/*
+extern graph_t *graph_read_dimacs(FILE *fp);
+extern graph_t *graph_read_dimacs_file(char *file);
+extern boolean graph_write_dimacs_ascii(graph_t *g, char *comment,FILE *fp);
+extern boolean graph_write_dimacs_ascii_file(graph_t *g,char *comment,
+ char *file);
+extern boolean graph_write_dimacs_binary(graph_t *g, char *comment,FILE *fp);
+extern boolean graph_write_dimacs_binary_file(graph_t *g, char *comment,
+ char *file);
+*/
+
+extern void graph_print(graph_t *g);
+extern boolean graph_test(graph_t *g, FILE *output);
+extern int graph_test_regular(graph_t *g);
+
+UNUSED_FUNCTION INLINE
+static int graph_subgraph_weight(graph_t *g,set_t s) {
+ int i,j;
+ int count=0;
+ setelement e;
+
+ for (i=0; iweights[i*ELEMENTSIZE+j];
+ e = e>>1;
+ }
+ }
+ }
+ return count;
+}
+
+UNUSED_FUNCTION INLINE
+static int graph_vertex_degree(graph_t *g, int v) {
+ return set_size(g->edges[v]);
+}
+
+#endif /* !CLIQUER_GRAPH_H */
diff --git a/src/cliquer/misc.h b/src/cliquer/misc.h
new file mode 100644
index 000000000..3f97fba2e
--- /dev/null
+++ b/src/cliquer/misc.h
@@ -0,0 +1,64 @@
+
+#ifndef CLIQUER_MISC_H
+#define CLIQUER_MISC_H
+
+#include "cliquerconf.h"
+
+/*
+ * We #define boolean instead of using a typedef because nauty.h uses it
+ * also. AFAIK, there is no way to check for an existing typedef, and
+ * re-typedefing is illegal (even when using exactly the same datatype!).
+ */
+#ifndef boolean
+#define boolean int
+#endif
+
+
+/*
+ * The original cliquer source has some functions incorrectly marked as unused,
+ * thus leave this undefined.
+ */
+#define UNUSED_FUNCTION
+
+
+/*
+ * Default inlining directive: "inline"
+ */
+#ifndef INLINE
+#define INLINE inline
+#endif
+
+
+#include
+#include
+
+#ifndef ASSERT
+#define ASSERT(expr) \
+ if (!(expr)) { \
+ fprintf(stderr,"cliquer file %s: line %d: assertion failed: " \
+ "(%s)\n",__FILE__,__LINE__,#expr); \
+ abort(); \
+ }
+#endif /* !ASSERT */
+
+
+#ifndef FALSE
+#define FALSE (0)
+#endif
+#ifndef TRUE
+#define TRUE (!FALSE)
+#endif
+
+
+#ifndef MIN
+#define MIN(a,b) (((a)<(b))?(a):(b))
+#endif
+#ifndef MAX
+#define MAX(a,b) (((a)>(b))?(a):(b))
+#endif
+#ifndef ABS
+#define ABS(v) (((v)<0)?(-(v)):(v))
+#endif
+
+#endif /* !CLIQUER_MISC_H */
+
diff --git a/src/cliquer/reorder.c b/src/cliquer/reorder.c
new file mode 100644
index 000000000..6f1a05e44
--- /dev/null
+++ b/src/cliquer/reorder.c
@@ -0,0 +1,425 @@
+
+/*
+ * 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
+
+#include
+
+#include
+
+
+/*
+ * 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>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; in; i++) {
+ reorder_set(g->edges[i],order);
+ tmp_e[order[i]]=g->edges[i];
+ tmp_w[order[i]]=g->weights[i];
+ }
+ for (i=0; in; 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 {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) {
+ free(used);
+ return FALSE;
+ }
+ if (used[order[i]]) {
+ free(used);
+ return FALSE;
+ }
+ used[order[i]]=TRUE;
+ }
+ for (i=0; in);
+}
+
+/*
+ * 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) {
+ int i,r;
+ int *new;
+ boolean *used;
+
+ new=calloc(g->n, sizeof(int));
+ used=calloc(g->n, sizeof(boolean));
+ for (i=0; i < g->n; i++) {
+ do {
+ r = igraph_rng_get_integer(igraph_rng_default(), 0, g->n - 1);
+ } while (used[r]);
+ new[i]=r;
+ used[r]=TRUE;
+ }
+ free(used);
+ return new;
+}
+
diff --git a/src/cliquer/reorder.h b/src/cliquer/reorder.h
new file mode 100644
index 000000000..5c06d31aa
--- /dev/null
+++ b/src/cliquer/reorder.h
@@ -0,0 +1,26 @@
+
+#ifndef CLIQUER_REORDER_H
+#define CLIQUER_REORDER_H
+
+#include "set.h"
+#include "graph.h"
+
+extern void reorder_set(set_t s,int *order);
+extern void reorder_graph(graph_t *g, int *order);
+extern int *reorder_duplicate(int *order,int n);
+extern void reorder_invert(int *order,int n);
+extern void reorder_reverse(int *order,int n);
+extern int *reorder_ident(int n);
+extern boolean reorder_is_bijection(int *order,int n);
+
+
+#define reorder_by_default reorder_by_greedy_coloring
+extern int *reorder_by_greedy_coloring(graph_t *g, boolean weighted);
+extern int *reorder_by_weighted_greedy_coloring(graph_t *g, boolean weighted);
+extern int *reorder_by_unweighted_greedy_coloring(graph_t *g,boolean weighted);
+extern int *reorder_by_degree(graph_t *g, boolean weighted);
+extern int *reorder_by_random(graph_t *g, boolean weighted);
+extern int *reorder_by_ident(graph_t *g, boolean weighted);
+extern int *reorder_by_reverse(graph_t *g, boolean weighted);
+
+#endif /* !CLIQUER_REORDER_H */
diff --git a/src/cliquer/set.h b/src/cliquer/set.h
new file mode 100644
index 000000000..bd6c73eee
--- /dev/null
+++ b/src/cliquer/set.h
@@ -0,0 +1,389 @@
+
+/*
+ * This file contains the set handling routines.
+ *
+ * Copyright (C) 2002 Sampo Niskanen, Patric Östergård.
+ * Licensed under the GNU GPL, read the file LICENSE for details.
+ */
+
+#ifndef CLIQUER_SET_H
+#define CLIQUER_SET_H
+
+#include
+#include
+#include
+#include
+#include "misc.h"
+
+/*
+ * Sets are arrays of setelement's (typically unsigned long int's) with
+ * representative bits for each value they can contain. The values
+ * are numbered 0,...,n-1.
+ */
+
+
+/*** Variable types and constants. ***/
+
+
+/*
+ * If setelement hasn't been declared:
+ * - use "unsigned long int" as setelement
+ * - try to deduce size from ULONG_MAX
+ */
+
+#ifndef ELEMENTSIZE
+typedef unsigned long int setelement;
+# if (ULONG_MAX == 65535)
+# define ELEMENTSIZE 16
+# elif (ULONG_MAX == 4294967295)
+# define ELEMENTSIZE 32
+# else
+# define ELEMENTSIZE 64
+# endif
+#endif /* !ELEMENTSIZE */
+
+typedef setelement * set_t;
+
+
+/*** Counting amount of 1 bits in a setelement ***/
+
+/* Array for amount of 1 bits in a byte. */
+static int set_bit_count[256] = {
+ 0,1,1,2,1,2,2,3,1,2,2,3,2,3,3,4,
+ 1,2,2,3,2,3,3,4,2,3,3,4,3,4,4,5,
+ 1,2,2,3,2,3,3,4,2,3,3,4,3,4,4,5,
+ 2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6,
+ 1,2,2,3,2,3,3,4,2,3,3,4,3,4,4,5,
+ 2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6,
+ 2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6,
+ 3,4,4,5,4,5,5,6,4,5,5,6,5,6,6,7,
+ 1,2,2,3,2,3,3,4,2,3,3,4,3,4,4,5,
+ 2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6,
+ 2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6,
+ 3,4,4,5,4,5,5,6,4,5,5,6,5,6,6,7,
+ 2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6,
+ 3,4,4,5,4,5,5,6,4,5,5,6,5,6,6,7,
+ 3,4,4,5,4,5,5,6,4,5,5,6,5,6,6,7,
+ 4,5,5,6,5,6,6,7,5,6,6,7,6,7,7,8 };
+
+/* The following macros assume that all higher bits are 0.
+ * They may in some cases be useful also on with other ELEMENTSIZE's,
+ * so we define them all. */
+#define SET_ELEMENT_BIT_COUNT_8(a) (set_bit_count[(a)])
+#define SET_ELEMENT_BIT_COUNT_16(a) (set_bit_count[(a)>>8] + \
+ set_bit_count[(a)&0xFF])
+#define SET_ELEMENT_BIT_COUNT_32(a) (set_bit_count[(a)>>24] + \
+ set_bit_count[((a)>>16)&0xFF] + \
+ set_bit_count[((a)>>8)&0xFF] + \
+ set_bit_count[(a)&0xFF])
+#define SET_ELEMENT_BIT_COUNT_64(a) (set_bit_count[(a)>>56] + \
+ set_bit_count[((a)>>48)&0xFF] + \
+ set_bit_count[((a)>>40)&0xFF] + \
+ set_bit_count[((a)>>32)&0xFF] + \
+ set_bit_count[((a)>>24)&0xFF] + \
+ set_bit_count[((a)>>16)&0xFF] + \
+ set_bit_count[((a)>>8)&0xFF] + \
+ set_bit_count[(a)&0xFF])
+#if (ELEMENTSIZE==64)
+# define SET_ELEMENT_BIT_COUNT(a) SET_ELEMENT_BIT_COUNT_64(a)
+# define FULL_ELEMENT ((setelement)0xFFFFFFFFFFFFFFFF)
+#elif (ELEMENTSIZE==32)
+# define SET_ELEMENT_BIT_COUNT(a) SET_ELEMENT_BIT_COUNT_32(a)
+# define FULL_ELEMENT ((setelement)0xFFFFFFFF)
+#elif (ELEMENTSIZE==16)
+# define SET_ELEMENT_BIT_COUNT(a) SET_ELEMENT_BIT_COUNT_16(a)
+# define FULL_ELEMENT ((setelement)0xFFFF)
+#else
+# error "SET_ELEMENT_BIT_COUNT(a) not defined for current ELEMENTSIZE"
+#endif
+
+
+
+/*** Macros and functions ***/
+
+/*
+ * Gives a value with bit x (counting from lsb up) set.
+ *
+ * Making this as a table might speed up things on some machines
+ * (though on most modern machines it's faster to shift instead of
+ * using memory). Making it a macro makes it easy to change.
+ */
+#define SET_BIT_MASK(x) ((setelement)1<<(x))
+
+
+
+/* Set element handling macros */
+
+#define SET_ELEMENT_INTERSECT(a,b) ((a)&(b))
+#define SET_ELEMENT_UNION(a,b) ((a)|(b))
+#define SET_ELEMENT_DIFFERENCE(a,b) ((a)&(~(b)))
+#define SET_ELEMENT_CONTAINS(e,v) ((e)&SET_BIT_MASK(v))
+
+
+/* Set handling macros */
+
+#define SET_ADD_ELEMENT(s,a) \
+ ((s)[(a)/ELEMENTSIZE] |= SET_BIT_MASK((a)%ELEMENTSIZE))
+#define SET_DEL_ELEMENT(s,a) \
+ ((s)[(a)/ELEMENTSIZE] &= ~SET_BIT_MASK((a)%ELEMENTSIZE))
+#define SET_CONTAINS_FAST(s,a) (SET_ELEMENT_CONTAINS((s)[(a)/ELEMENTSIZE], \
+ (a)%ELEMENTSIZE))
+#define SET_CONTAINS(s,a) (((a)0);
+
+ n=(size/ELEMENTSIZE+1)+1;
+ s=calloc(n,sizeof(setelement));
+ s[0]=size;
+
+ return &(s[1]);
+}
+
+/*
+ * set_free()
+ *
+ * Free the memory associated with set s.
+ */
+UNUSED_FUNCTION INLINE
+static void set_free(set_t s) {
+ ASSERT(s!=NULL);
+ free(&(s[-1]));
+}
+
+/*
+ * set_resize()
+ *
+ * Resizes set s to given size. If the size is less than SET_MAX_SIZE(s),
+ * the last elements are dropped.
+ *
+ * Returns a pointer to the new set.
+ */
+UNUSED_FUNCTION INLINE
+static set_t set_resize(set_t s, int size) {
+ int n;
+
+ ASSERT(size>0);
+
+ n=(size/ELEMENTSIZE+1);
+ s=((setelement *)realloc(s-1,(n+1)*sizeof(setelement)))+1;
+
+ if (n>SET_ARRAY_LENGTH(s))
+ memset(s+SET_ARRAY_LENGTH(s),0,
+ (n-SET_ARRAY_LENGTH(s))*sizeof(setelement));
+ if (size < SET_MAX_SIZE(s))
+ s[(size-1)/ELEMENTSIZE] &= (FULL_ELEMENT >>
+ (ELEMENTSIZE-size%ELEMENTSIZE));
+ s[-1]=size;
+
+ return s;
+}
+
+/*
+ * set_size()
+ *
+ * Returns the number of elements in set s.
+ */
+UNUSED_FUNCTION INLINE
+static int set_size(set_t s) {
+ int count=0;
+ setelement *c;
+
+ for (c=s; c < s+SET_ARRAY_LENGTH(s); c++)
+ count+=SET_ELEMENT_BIT_COUNT(*c);
+ return count;
+}
+
+/*
+ * set_duplicate()
+ *
+ * Returns a newly allocated duplicate of set s.
+ */
+UNUSED_FUNCTION INLINE
+static set_t set_duplicate(set_t s) {
+ set_t new;
+
+ new=set_new(SET_MAX_SIZE(s));
+ memcpy(new,s,SET_ARRAY_LENGTH(s)*sizeof(setelement));
+ return new;
+}
+
+/*
+ * set_copy()
+ *
+ * Copies set src to dest. If dest is NULL, is equal to set_duplicate.
+ * If dest smaller than src, it is freed and a new set of the same size as
+ * src is returned.
+ */
+UNUSED_FUNCTION INLINE
+static set_t set_copy(set_t dest,set_t src) {
+ if (dest==NULL)
+ return set_duplicate(src);
+ if (SET_MAX_SIZE(dest)=0) {
+ * // i is in set s
+ * }
+ */
+UNUSED_FUNCTION INLINE
+static int set_return_next(set_t s, int n) {
+ if (n<0)
+ n=0;
+ else
+ n++;
+ if (n >= SET_MAX_SIZE(s))
+ return -1;
+
+ while (n%ELEMENTSIZE) {
+ if (SET_CONTAINS(s,n))
+ return n;
+ n++;
+ if (n >= SET_MAX_SIZE(s))
+ return -1;
+ }
+
+ while (s[n/ELEMENTSIZE]==0) {
+ n+=ELEMENTSIZE;
+ if (n >= SET_MAX_SIZE(s))
+ return -1;
+ }
+ while (!SET_CONTAINS(s,n)) {
+ n++;
+ if (n >= SET_MAX_SIZE(s))
+ return -1;
+ }
+ return n;
+}
+
+
+/*
+ * set_print()
+ *
+ * Prints the size and contents of set s to stdout.
+ * Mainly useful for debugging purposes and trivial output.
+ */
+/*
+UNUSED_FUNCTION
+static void set_print(set_t s) {
+ int i;
+ printf("size=%d(max %d)",set_size(s),(int)SET_MAX_SIZE(s));
+ for (i=0; i
@@ -326,7 +327,185 @@ int igraph_i_cliques(const igraph_t *graph, igraph_vector_ptr_t *res,
*/
int igraph_cliques(const igraph_t *graph, igraph_vector_ptr_t *res,
igraph_integer_t min_size, igraph_integer_t max_size) {
- return igraph_i_cliques(graph, res, min_size, max_size, 0);
+ return igraph_i_cliquer_cliques(graph, res, min_size, max_size);
+}
+
+
+/**
+ * \function igraph_clique_size_hist
+ * \brief Count cliques of each size in the graph
+ *
+ *
+ * Cliques are fully connected subgraphs of a graph.
+ *
+ * The current implementation of this function
+ * uses version 1.21 of the Cliquer library by Sampo Niskanen and
+ * Patric R. J. Östergård, http://users.aalto.fi/~pat/cliquer.html
+ *
+ * \param graph The input graph.
+ * \param hist Pointer to an initialized vector. The result will be stored
+ * here. The first element will store the number of size-1 cliques, the second
+ * element the number of size-2 cliques, etc. For cliques smaller than \c min_size,
+ * zero counts will be returned.
+ * \param min_size Integer giving the minimum size of the cliques to be
+ * returned. If negative or zero, no lower bound will be used.
+ * \param max_size Integer giving the maximum size of the cliques to be
+ * returned. If negative or zero, no upper bound will be used.
+ * \return Error code.
+ *
+ * \sa \ref igraph_cliques() and \ref igraph_cliques_callback()
+ *
+ * Time complexity: Exponential
+ *
+ */
+int igraph_clique_size_hist(const igraph_t *graph, igraph_vector_t *hist,
+ igraph_integer_t min_size, igraph_integer_t max_size) {
+ return igraph_i_cliquer_histogram(graph, hist, min_size, max_size);
+}
+
+
+/**
+ * \function igraph_cliques_callback
+ * \brief Calls a function for each clique in the graph.
+ *
+ *
+ * Cliques are fully connected subgraphs of a graph. This function
+ * enumerates all cliques within the given size range and calls
+ * \p cliquehandler_fn for each of them. The cliques are passed to the
+ * callback function as an igraph_vector_t *. Destroying and
+ * freeing this vector is left up to the user. Use \ref igraph_vector_destroy()
+ * to destroy it first, then free it using \ref igraph_free().
+ *
+ * The current implementation of this function
+ * uses version 1.21 of the Cliquer library by Sampo Niskanen and
+ * Patric R. J. Östergård, http://users.aalto.fi/~pat/cliquer.html
+ *
+ * \param graph The input graph.
+ * \param min_size Integer giving the minimum size of the cliques to be
+ * returned. If negative or zero, no lower bound will be used.
+ * \param max_size Integer giving the maximum size of the cliques to be
+ * returned. If negative or zero, no upper bound will be used.
+ * \param cliquehandler_fn Callback function to be called for each clique.
+ * See also igraph_clique_handler_t.
+ * \param arg Extra argument to supply to \p cliquehandler_fn.
+ * \return Error code.
+ *
+ * \sa \ref igraph_cliques()
+ *
+ * Time complexity: Exponential
+ *
+ */
+int igraph_cliques_callback(const igraph_t *graph,
+ igraph_integer_t min_size, igraph_integer_t max_size,
+ igraph_clique_handler_t *cliquehandler_fn, void *arg)
+{
+ return igraph_i_cliquer_callback(graph, min_size, max_size, cliquehandler_fn, arg);
+}
+
+
+/**
+ * \function igraph_weighted_cliques
+ * \brief Find all cliques in a given weight range in a vertex weighted graph
+ *
+ *
+ * Cliques are fully connected subgraphs of a graph.
+ * The weight of a clique is the sum of the weights
+ * of individual vertices within the clique.
+ *
+ * The current implementation of this function
+ * uses version 1.21 of the Cliquer library by Sampo Niskanen and
+ * Patric R. J. Östergård, http://users.aalto.fi/~pat/cliquer.html
+ *
+ * Only positive integer vertex weights are supported.
+ *
+ * \param graph The input graph.
+ * \param vertex_weights A vector of vertex weights. The current implementation
+ * will truncate all weights to their integer parts.
+ * \param res Pointer to a pointer vector, the result will be stored
+ * here, ie. \c res will contain pointers to \c igraph_vector_t
+ * objects which contain the indices of vertices involved in a clique.
+ * The pointer vector will be resized if needed but note that the
+ * objects in the pointer vector will not be freed.
+ * \param min_weight Integer giving the minimum weight of the cliques to be
+ * returned. If negative or zero, no lower bound will be used.
+ * \param max_weight Integer giving the maximum weight of the cliques to be
+ * returned. If negative or zero, no upper bound will be used.
+ * \param maximal If true, only maximal cliques will be returned
+ * \return Error code.
+ *
+ * \sa \ref igraph_cliques(), \ref igraph_maximal_cliques()
+ *
+ * Time complexity: Exponential
+ *
+ */
+int igraph_weighted_cliques(const igraph_t *graph,
+ const igraph_vector_t *vertex_weights, igraph_vector_ptr_t *res,
+ igraph_real_t min_weight, igraph_real_t max_weight, igraph_bool_t maximal)
+{
+ return igraph_i_weighted_cliques(graph, vertex_weights, res, min_weight, max_weight, maximal);
+}
+
+
+/**
+ * \function igraph_largest_weighted_cliques
+ * \brief Finds the largest weight clique(s) in a graph.
+ *
+ *
+ * Finds the clique(s) having the largest weight in the graph.
+ *
+ * The current implementation of this function
+ * uses version 1.21 of the Cliquer library by Sampo Niskanen and
+ * Patric R. J. Östergård, http://users.aalto.fi/~pat/cliquer.html
+ *
+ * Only positive integer vertex weights are supported.
+ *
+ * \param graph The input graph.
+ * \param vertex_weights A vector of vertex weights. The current implementation
+ * will truncate all weights to their integer parts.
+ * \param res Pointer to a pointer vector, the result will be stored
+ * here, ie. \c res will contain pointers to \c igraph_vector_t
+ * objects which contain the indices of vertices involved in a clique.
+ * The pointer vector will be resized if needed but note that the
+ * objects in the pointer vector will not be freed.
+ * \return Error code.
+ *
+ * \sa \ref igraph_weighted_cliques(), \ref igraph_weighted_clique_number(), \ref igraph_largest_cliques()
+ *
+ * Time complexity: TODO
+ */
+int igraph_largest_weighted_cliques(const igraph_t *graph,
+ const igraph_vector_t *vertex_weights, igraph_vector_ptr_t *res)
+{
+ return igraph_i_largest_weighted_cliques(graph, vertex_weights, res);
+}
+
+
+/**
+ * \function igraph_weighted_clique_number
+ * \brief Find the weight of the largest weight clique in the graph
+ *
+ * The current implementation of this function
+ * uses version 1.21 of the Cliquer library by Sampo Niskanen and
+ * Patric R. J. Östergård, http://users.aalto.fi/~pat/cliquer.html
+ *
+ * Only positive integer vertex weights are supported.
+ *
+ * \param graph The input graph.
+ * \param vertex_weights A vector of vertex weights. The current implementation
+ * will truncate all weights to their integer parts.
+ * \param res The largest weight will be returned to the \c igraph_real_t
+ * pointed to by this variable.
+ * \return Error code.
+ *
+ * \sa \ref igraph_weighted_cliques(), \ref igraph_largest_weighted_cliques(), \ref igraph_clique_number()
+ *
+ * Time complexity: TODO
+ *
+ */
+int igraph_weighted_clique_number(const igraph_t *graph,
+ const igraph_vector_t *vertex_weights, igraph_real_t *res)
+{
+ return igraph_i_weighted_clique_number(graph, vertex_weights, res);
}
typedef int(*igraph_i_maximal_clique_func_t)(const igraph_vector_t*, void*, igraph_bool_t*);
diff --git a/src/igraph_cliquer.c b/src/igraph_cliquer.c
new file mode 100644
index 000000000..f0cf143c5
--- /dev/null
+++ b/src/igraph_cliquer.c
@@ -0,0 +1,375 @@
+
+#include "igraph_cliquer.h"
+#include "igraph_memory.h"
+#include "igraph_constants.h"
+#include "igraph_interrupt_internal.h"
+#include "cliquer/cliquer.h"
+#include "config.h"
+
+#include
+
+
+/* Call this to allow for interruption in Cliquer callback functions */
+#define CLIQUER_ALLOW_INTERRUPTION() \
+ { \
+ if (igraph_i_interruption_handler) \
+ if (igraph_allow_interruption(NULL) != IGRAPH_SUCCESS) { \
+ cliquer_interrupted = 1; \
+ return FALSE; \
+ } \
+ }
+
+/* Interruptable Cliquer functions must be wrapped in CLIQUER_INTERRUPTABLE when called */
+#define CLIQUER_INTERRUPTABLE(x) \
+ { \
+ cliquer_interrupted = 0; \
+ x; \
+ if (cliquer_interrupted) return IGRAPH_INTERRUPTED; \
+ }
+
+
+/* Nonzero value signals interuption from Cliquer callback function */
+IGRAPH_THREAD_LOCAL static int cliquer_interrupted;
+
+
+/* For use with IGRAPH_FINALLY */
+static void free_clique_list(igraph_vector_ptr_t *vp) {
+ igraph_integer_t i, len;
+ len = igraph_vector_ptr_size(vp);
+ for (i=0; i < len; ++i)
+ igraph_vector_destroy((igraph_vector_t *) VECTOR(*vp)[i]);
+ igraph_vector_ptr_free_all(vp);
+}
+
+/* We shall use this option struct for all calls to Cliquer */
+IGRAPH_THREAD_LOCAL static clique_options igraph_cliquer_opt = {
+ reorder_by_default, NULL, NULL, NULL, NULL, NULL, NULL, 0
+};
+
+
+/* Convert an igraph graph to a Cliquer graph */
+static void igraph_to_cliquer(const igraph_t *ig, graph_t **cg) {
+ igraph_integer_t vcount, ecount;
+ int i;
+
+ if (igraph_is_directed(ig))
+ IGRAPH_WARNING("Edge directions are ignored for clique calculations");
+
+ vcount = igraph_vcount(ig);
+ ecount = igraph_ecount(ig);
+
+ *cg = graph_new(vcount);
+
+ for (i=0; i < ecount; ++i) {
+ long s, t;
+ s = IGRAPH_FROM(ig, i);
+ t = IGRAPH_TO(ig, i);
+ if (s != t)
+ GRAPH_ADD_EDGE(*cg, s, t);
+ }
+}
+
+
+/* Copy weights to a Cliquer graph */
+static int set_weights(const igraph_vector_t *vertex_weights, graph_t *g) {
+ int i;
+
+ assert(vertex_weights != NULL);
+
+ if (igraph_vector_size(vertex_weights) != g->n)
+ IGRAPH_ERROR("Invalid vertex weight vector length", IGRAPH_EINVAL);
+
+ for (i=0; i < g->n; ++i) {
+ g->weights[i] = VECTOR(*vertex_weights)[i];
+ if (g->weights[i] != VECTOR(*vertex_weights)[i])
+ IGRAPH_WARNING("Only integer vertex weights are supported; weights will be truncated to their integer parts");
+ if (g->weights[i] <= 0)
+ IGRAPH_ERROR("Vertex weights must be positive", IGRAPH_EINVAL);
+ }
+
+ return IGRAPH_SUCCESS;
+}
+
+
+/* Find all cliques. */
+
+static boolean collect_cliques_callback(set_t s, graph_t *g, clique_options *opt) {
+ igraph_vector_ptr_t *list;
+ igraph_vector_t *clique;
+ int i, j;
+
+ CLIQUER_ALLOW_INTERRUPTION();
+
+ list = (igraph_vector_ptr_t *) opt->user_data;
+ clique = (igraph_vector_t *) malloc(sizeof(igraph_vector_t));
+ igraph_vector_init(clique, set_size(s));
+
+ i = -1; j = 0;
+ while ((i = set_return_next(s,i)) >= 0)
+ VECTOR(*clique)[j++] = i;
+
+ igraph_vector_ptr_push_back(list, clique);
+
+ return TRUE;
+}
+
+int igraph_i_cliquer_cliques(const igraph_t *graph, igraph_vector_ptr_t *res,
+ igraph_integer_t min_size, igraph_integer_t max_size)
+{
+ graph_t *g;
+ igraph_integer_t vcount = igraph_vcount(graph);
+
+ if (vcount == 0) {
+ igraph_vector_ptr_clear(res);
+ return IGRAPH_SUCCESS;
+ }
+
+ if (min_size <= 0) min_size = 1;
+ if (max_size <= 0) max_size = 0;
+
+ if (max_size > 0 && max_size < min_size)
+ IGRAPH_ERROR("max_size must not be smaller than min_size", IGRAPH_EINVAL);
+
+ igraph_to_cliquer(graph, &g);
+ IGRAPH_FINALLY(graph_free, g);
+
+ igraph_vector_ptr_clear(res);
+ igraph_cliquer_opt.user_data = res;
+ igraph_cliquer_opt.user_function = &collect_cliques_callback;
+
+ IGRAPH_FINALLY(free_clique_list, res);
+ CLIQUER_INTERRUPTABLE(clique_unweighted_find_all(g, min_size, max_size, /* maximal= */ FALSE, &igraph_cliquer_opt));
+ IGRAPH_FINALLY_CLEAN(1);
+
+ graph_free(g);
+ IGRAPH_FINALLY_CLEAN(1);
+
+ return IGRAPH_SUCCESS;
+}
+
+
+/* Count cliques of each size. */
+
+static boolean count_cliques_callback(set_t s, graph_t *g, clique_options *opt) {
+ igraph_vector_t *hist;
+
+ CLIQUER_ALLOW_INTERRUPTION();
+
+ hist = (igraph_vector_t *) opt->user_data;
+ VECTOR(*hist)[set_size(s)-1] += 1;
+
+ return TRUE;
+}
+
+int igraph_i_cliquer_histogram(const igraph_t *graph, igraph_vector_t *hist,
+ igraph_integer_t min_size, igraph_integer_t max_size)
+{
+ graph_t *g;
+ int i;
+ igraph_integer_t vcount = igraph_vcount(graph);
+
+ if (vcount == 0) {
+ igraph_vector_clear(hist);
+ return IGRAPH_SUCCESS;
+ }
+
+ if (min_size <= 0) min_size = 1;
+ if (max_size <= 0) max_size = vcount; /* also used for initial hist vector size, do not set to zero */
+
+ if (max_size < min_size)
+ IGRAPH_ERROR("max_size must not be smaller than min_size", IGRAPH_EINVAL);
+
+ igraph_to_cliquer(graph, &g);
+ IGRAPH_FINALLY(graph_free, g);
+
+ igraph_vector_resize(hist, max_size);
+ igraph_vector_null(hist);
+ igraph_cliquer_opt.user_data = hist;
+ igraph_cliquer_opt.user_function = &count_cliques_callback;
+
+ CLIQUER_INTERRUPTABLE(clique_unweighted_find_all(g, min_size, max_size, /* maximal= */ FALSE, &igraph_cliquer_opt));
+
+ for (i=max_size; i > 0; --i)
+ if (VECTOR(*hist)[i-1] > 0)
+ break;
+ igraph_vector_resize(hist, i);
+ igraph_vector_resize_min(hist);
+
+ graph_free(g);
+ IGRAPH_FINALLY_CLEAN(1);
+
+ return IGRAPH_SUCCESS;
+}
+
+
+/* Call function for each clique. */
+
+struct callback_data {
+ igraph_clique_handler_t *handler;
+ void *arg;
+};
+
+static boolean callback_callback(set_t s, graph_t *g, clique_options *opt) {
+ igraph_vector_t *clique;
+ struct callback_data *cd;
+ int i, j;
+
+ CLIQUER_ALLOW_INTERRUPTION();
+
+ cd = (struct callback_data *) opt->user_data;
+
+ clique = (igraph_vector_t *) malloc(sizeof(igraph_vector_t));
+ igraph_vector_init(clique, set_size(s));
+
+ i = -1; j = 0;
+ while ((i = set_return_next(s,i)) >= 0)
+ VECTOR(*clique)[j++] = i;
+
+ return (*(cd->handler))(clique, cd->arg);
+}
+
+int igraph_i_cliquer_callback(const igraph_t *graph,
+ igraph_integer_t min_size, igraph_integer_t max_size,
+ igraph_clique_handler_t *cliquehandler_fn, void *arg)
+{
+ graph_t *g;
+ struct callback_data cd;
+ igraph_integer_t vcount = igraph_vcount(graph);
+
+ if (vcount == 0)
+ return IGRAPH_SUCCESS;
+
+ if (min_size <= 0) min_size = 1;
+ if (max_size <= 0) max_size = 0;
+
+ if (max_size > 0 && max_size < min_size)
+ IGRAPH_ERROR("max_size must not be smaller than min_size", IGRAPH_EINVAL);
+
+ igraph_to_cliquer(graph, &g);
+ IGRAPH_FINALLY(graph_free, g);
+
+ cd.handler = cliquehandler_fn;
+ cd.arg = arg;
+ igraph_cliquer_opt.user_data = &cd;
+ igraph_cliquer_opt.user_function = &callback_callback;
+
+ CLIQUER_INTERRUPTABLE(clique_unweighted_find_all(g, min_size, max_size, /* maximal= */ FALSE, &igraph_cliquer_opt));
+
+ graph_free(g);
+ IGRAPH_FINALLY_CLEAN(1);
+
+ return IGRAPH_SUCCESS;
+}
+
+
+/* Find weighted cliques in given weight range. */
+
+int igraph_i_weighted_cliques(const igraph_t *graph,
+ const igraph_vector_t *vertex_weights, igraph_vector_ptr_t *res,
+ igraph_real_t min_weight, igraph_real_t max_weight, igraph_bool_t maximal)
+{
+ graph_t *g;
+ igraph_integer_t vcount = igraph_vcount(graph);
+
+ if (vcount == 0) {
+ igraph_vector_ptr_clear(res);
+ return IGRAPH_SUCCESS;
+ }
+
+ if (min_weight != (int) min_weight) {
+ IGRAPH_WARNING("Only integer vertex weights are supported; the minimum weight will be truncated to its integer part");
+ min_weight = (int) min_weight;
+ }
+
+ if (max_weight != (int) max_weight) {
+ IGRAPH_WARNING("Only integer vertex weights are supported; the maximum weight will be truncated to its integer part");
+ max_weight = (int) max_weight;
+ }
+
+ if (min_weight <= 0) min_weight = 1;
+ if (max_weight <= 0) max_weight = 0;
+
+ if (max_weight > 0 && max_weight < min_weight)
+ IGRAPH_ERROR("max_weight must not be smaller than min_weight", IGRAPH_EINVAL);
+
+ igraph_to_cliquer(graph, &g);
+ IGRAPH_FINALLY(graph_free, g);
+
+ IGRAPH_CHECK(set_weights(vertex_weights, g));
+
+ igraph_vector_ptr_clear(res);
+ igraph_cliquer_opt.user_data = res;
+ igraph_cliquer_opt.user_function = &collect_cliques_callback;
+
+ IGRAPH_FINALLY(free_clique_list, res);
+ CLIQUER_INTERRUPTABLE(clique_find_all(g, min_weight, max_weight, maximal, &igraph_cliquer_opt));
+ IGRAPH_FINALLY_CLEAN(1);
+
+ graph_free(g);
+ IGRAPH_FINALLY_CLEAN(1);
+
+ return IGRAPH_SUCCESS;
+}
+
+
+/* Find largest weighted cliques. */
+
+int igraph_i_largest_weighted_cliques(const igraph_t *graph,
+ const igraph_vector_t *vertex_weights, igraph_vector_ptr_t *res)
+{
+ graph_t *g;
+ igraph_integer_t vcount = igraph_vcount(graph);
+
+ if (vcount == 0) {
+ igraph_vector_ptr_clear(res);
+ return IGRAPH_SUCCESS;
+ }
+
+ igraph_to_cliquer(graph, &g);
+ IGRAPH_FINALLY(graph_free, g);
+
+ IGRAPH_CHECK(set_weights(vertex_weights, g));
+
+ igraph_vector_ptr_clear(res);
+ igraph_cliquer_opt.user_data = res;
+ igraph_cliquer_opt.user_function = &collect_cliques_callback;
+
+ IGRAPH_FINALLY(free_clique_list, res);
+ CLIQUER_INTERRUPTABLE(clique_find_all(g, 0, 0, FALSE, &igraph_cliquer_opt));
+ IGRAPH_FINALLY_CLEAN(1);
+
+ graph_free(g);
+ IGRAPH_FINALLY_CLEAN(1);
+
+ return IGRAPH_SUCCESS;
+}
+
+
+/* Find weight of largest weight clique. */
+
+int igraph_i_weighted_clique_number(const igraph_t *graph,
+ const igraph_vector_t *vertex_weights, igraph_real_t *res)
+{
+ graph_t *g;
+ igraph_integer_t vcount = igraph_vcount(graph);
+
+ if (vcount == 0) {
+ *res = 0;
+ return IGRAPH_SUCCESS;
+ }
+
+ igraph_to_cliquer(graph, &g);
+ IGRAPH_FINALLY(graph_free, g);
+
+ IGRAPH_CHECK(set_weights(vertex_weights, g));
+
+ igraph_cliquer_opt.user_function = NULL;
+
+ /* we are not using a callback function, thus this is not interruptable */
+ *res = clique_max_weight(g, &igraph_cliquer_opt);
+
+ graph_free(g);
+ IGRAPH_FINALLY_CLEAN(1);
+
+ return IGRAPH_SUCCESS;
+}
diff --git a/src/igraph_cliquer.h b/src/igraph_cliquer.h
new file mode 100644
index 000000000..5379aec89
--- /dev/null
+++ b/src/igraph_cliquer.h
@@ -0,0 +1,29 @@
+#ifndef IGRAPH_CLIQUER_H
+#define IGRAPH_CLIQUER_H
+
+#include "igraph_types_internal.h"
+#include "igraph_interface.h"
+#include "igraph_cliques.h"
+
+int igraph_i_cliquer_cliques(const igraph_t *graph, igraph_vector_ptr_t *res,
+ igraph_integer_t min_size, igraph_integer_t max_size);
+
+int igraph_i_cliquer_histogram(const igraph_t *graph, igraph_vector_t *hist,
+ igraph_integer_t min_size, igraph_integer_t max_size);
+
+int igraph_i_cliquer_callback(const igraph_t *graph,
+ igraph_integer_t min_size, igraph_integer_t max_size,
+ igraph_clique_handler_t *cliquehandler_fn, void *arg);
+
+int igraph_i_weighted_cliques(const igraph_t *graph,
+ const igraph_vector_t *vertex_weights, igraph_vector_ptr_t *res,
+ igraph_real_t min_weight, igraph_real_t max_weight, igraph_bool_t maximal);
+
+int igraph_i_largest_weighted_cliques(const igraph_t *graph,
+ const igraph_vector_t *vertex_weights, igraph_vector_ptr_t *res);
+
+int igraph_i_weighted_clique_number(const igraph_t *graph,
+ const igraph_vector_t *vertex_weights, igraph_real_t *res);
+
+#endif // IGRAPH_CLIQUER_H
+
diff --git a/tests/cliques.at b/tests/cliques.at
index bfcdbee26..7f054e785 100644
--- a/tests/cliques.at
+++ b/tests/cliques.at
@@ -3,20 +3,20 @@
# Test suite for the IGraph library.
# Copyright (C) 2005-2012 Gabor Csardi
# 334 Harvard street, Cambridge, MA 02139 USA
-#
+#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
-#
+#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
-#
+#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
-# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
# 02110-1301 USA
# Macros
@@ -36,26 +36,31 @@ AT_CLEANUP
AT_SETUP([More maximal cliques (igraph_maximal_cliques):])
AT_KEYWORDS([igraph_maximal_cliques cliques maximal cliques])
-AT_COMPILE_CHECK([simple/igraph_maximal_cliques2.c],
+AT_COMPILE_CHECK([simple/igraph_maximal_cliques2.c],
[simple/igraph_maximal_cliques2.out])
AT_CLEANUP
AT_SETUP([Maximal cliques 3 (igraph_maximal_cliques):])
AT_KEYWORDS([igraph_maximal_cliques cliques maximal cliques])
-AT_COMPILE_CHECK([simple/igraph_maximal_cliques3.c],
+AT_COMPILE_CHECK([simple/igraph_maximal_cliques3.c],
[simple/igraph_maximal_cliques3.out])
AT_CLEANUP
AT_SETUP([Maximal cliques for a subset (igraph_maximal_cliques):])
AT_KEYWORDS([igraph_maximal_cliques cliques maximal cliques])
-AT_COMPILE_CHECK([simple/igraph_maximal_cliques4.c],
+AT_COMPILE_CHECK([simple/igraph_maximal_cliques4.c],
[simple/igraph_maximal_cliques4.out])
AT_CLEANUP
+AT_SETUP([Weighted cliques (igraph_weighted_cliques):])
+AT_KEYWORDS([igraph_weighted_cliques cliques])
+AT_COMPILE_CHECK([simple/igraph_weighted_cliques.c],
+ [simple/igraph_weighted_cliques.out])
+AT_CLEANUP
+
AT_SETUP([Calculating independent vertex sets (igraph_independent_vertex_sets): ])
AT_KEYWORDS([igraph_independent_vertex_sets,
igraph_maximal_independent_vertex_sets,
igraph_independence_number])
AT_COMPILE_CHECK([simple/igraph_independent_sets.c], [simple/igraph_independent_sets.out])
AT_CLEANUP
-