From 6ba6627e17d35bd13cf6c61e5205249207e630bf Mon Sep 17 00:00:00 2001 From: Julius Date: Fri, 7 Feb 2020 15:11:13 +0100 Subject: [PATCH 01/30] add: initial set up for implementing Bron Kerbosch algorithm --- Makefile.am | 2 + src/cliques.c | 279 +++++++++++++++++++++++++++++++++++++++++++++++++ src/cliques.h | 19 ++++ src/digraphs.c | 8 ++ 4 files changed, 308 insertions(+) create mode 100644 src/cliques.c create mode 100644 src/cliques.h diff --git a/Makefile.am b/Makefile.am index d4238c059..84da459d9 100644 --- a/Makefile.am +++ b/Makefile.am @@ -27,6 +27,7 @@ pkginclude_HEADERS += src/digraphs-debug.h pkginclude_HEADERS += src/digraphs.h pkginclude_HEADERS += src/homos-graphs.h pkginclude_HEADERS += src/homos.h +pkginclude_HEADERS += src/cliques.h pkginclude_HEADERS += src/perms.h pkginclude_HEADERS += src/planar.h pkginclude_HEADERS += src/schreier-sims.h @@ -50,6 +51,7 @@ digraphs_la_SOURCES = src/digraphs.c digraphs_la_SOURCES += src/bitarray.c digraphs_la_SOURCES += src/conditions.c digraphs_la_SOURCES += src/homos.c +digraphs_la_SOURCES += src/cliques.c digraphs_la_SOURCES += src/homos-graphs.c digraphs_la_SOURCES += src/perms.c digraphs_la_SOURCES += src/planar.c diff --git a/src/cliques.c b/src/cliques.c new file mode 100644 index 000000000..d7218f0c5 --- /dev/null +++ b/src/cliques.c @@ -0,0 +1,279 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// cliques.c di/cliques Julius Jonusas +// +// Copyright (C) 2020 - Julius Jonusas +// +// This file is free software, see the digraphs/LICENSE. + + +// C headers +#include // for CHAR_BIT +#include // for longjmp, setjmp, jmp_buf +#include // for true, false, bool +#include // for NULL +#include // for uint16_t, uint64_t +#include // for malloc, NULL +#include // for time + +// GAP headers +#include "src/compiled.h" + +// Digraphs package headers +#include "bitarray.h" // for BitArray +#include "conditions.h" // for Conditions +#include "digraphs-config.h" // for DIGRAPHS_HAVE___BUILTIN_CTZLL +#include "digraphs-debug.h" // for DIGRAPHS_ASSERT +#include "homos-graphs.h" // for Digraph, Graph, . . . +#include "perms.h" // for MAXVERTS, UNDEFINED, PermColl, Perm +#include "schreier-sims.h" // for PermColl, . . . +#include "cliques.h" + +//////////////////////////////////////////////////////////////////////////////// +// Forward declarations +//////////////////////////////////////////////////////////////////////////////// + +// Defined in digraphs.h +Int DigraphNrVertices(Obj); +Obj FuncOutNeighbours(Obj, Obj); + +// GAP level things, imported in digraphs.c +extern Obj IsDigraph; +extern Obj DIGRAPHS_ValidateVertexColouring; +extern Obj Infinity; +extern Obj IsSymmetricDigraph; +extern Obj GeneratorsOfGroup; +extern Obj AutomorphismGroup; +extern Obj IsPermGroup; +extern Obj IsDigraphAutomorphism; +extern Obj LargestMovedPointPerms; + +//////////////////////////////////////////////////////////////////////////////// +// Global variables +//////////////////////////////////////////////////////////////////////////////// + +static Obj GAP_FUNC; // Variable to hold a GAP level hook function + +static Obj (*HOOK)(void*, // HOOK function applied to every homo found + const uint16_t, + const uint8_t*); +static void* USER_PARAM; // a USER_PARAM for the hook + +// Values in MAP are restricted to those positions in IMAGE_RESTRICT +static jmp_buf OUTOFHERE; // so we can jump out of the deepest + + +static Digraph* DIGRAPH; // Digraphs to hold incoming GAP digraphs + +static Graph* GRAPH; // Graphs to hold incoming GAP symmetric digraphs + + +//////////////////////////////////////////////////////////////////////////////// +// +//////////////////////////////////////////////////////////////////////////////// + +static void BronKerbosch() { + + // TODO + +} + + +static Obj clique_hook_collect(void* user_param, uint16_t const nr, uint8_t const* clique) { + UInt2* ptr; + UInt i; + Obj c; + + if (TNUM_OBJ((Obj) user_param) == T_PLIST_EMPTY) { + RetypeBag(user_param, T_PLIST); + } + + c = NewEmptyPlist(); + for(i = 0; i < nr; i++) { + PushPlist(c, ObjInt_UInt8(clique[i])); + } + + ASS_LIST(user_param, LEN_LIST(user_param) + 1, c); + return False; +} + +static Obj clique_hook_gap(void* user_param, uint16_t const nr, uint8_t const* clique) { + UInt2* ptr; + UInt i; + Obj c; + + c = NewEmptyPlist(); + for(i = 0; i < nr; i++) { + PushPlist(c, ObjInt_UInt8(clique[i])); + } + + return CALL_2ARGS(GAP_FUNC, user_param, c); +} + + +static void init_clique_graph_from_digraph_obj(Graph* const graph, + Obj digraph_obj) { + DIGRAPHS_ASSERT(graph != NULL); + DIGRAPHS_ASSERT(CALL_1ARGS(IsDigraph, digraph_obj) == True); + DIGRAPHS_ASSERT(CALL_1ARGS(IsSymmetricDigraph, digraph_obj) == True); + UInt const nr = DigraphNrVertices(digraph_obj); + Obj out = FuncOutNeighbours(0L, digraph_obj); + DIGRAPHS_ASSERT(nr < MAXVERTS); + DIGRAPHS_ASSERT(IS_PLIST(out)); + clear_graph(graph, nr); + + for (uint16_t i = 1; i <= nr; i++) { + Obj nbs = ELM_PLIST(out, i); + DIGRAPHS_ASSERT(IS_LIST(nbs)); + for (uint16_t j = 1; j <= LEN_LIST(nbs); j++) { + DIGRAPHS_ASSERT(IS_INTOBJ(ELM_LIST(nbs, j))); + add_edge_graph(graph, i - 1, INT_INTOBJ(ELM_LIST(nbs, j)) - 1); + } + } +} + +static void init_clique_digraph_from_digraph_obj(Digraph* const digraph, + Obj digraph_obj) { + DIGRAPHS_ASSERT(digraph != NULL); + DIGRAPHS_ASSERT(CALL_1ARGS(IsDigraph, digraph_obj) == True); + UInt const nr = DigraphNrVertices(digraph_obj); + Obj out = FuncOutNeighbours(0L, digraph_obj); + DIGRAPHS_ASSERT(nr < MAXVERTS); + DIGRAPHS_ASSERT(IS_PLIST(out)); + clear_digraph(digraph, nr); + + for (uint16_t i = 1; i <= nr; i++) { + Obj nbs = ELM_PLIST(out, i); + DIGRAPHS_ASSERT(IS_LIST(nbs)); + for (uint16_t j = 1; j <= LEN_LIST(nbs); j++) { + DIGRAPHS_ASSERT(IS_INTOBJ(ELM_LIST(nbs, j))); + add_edge_digraph(digraph, i - 1, INT_INTOBJ(ELM_LIST(nbs, j)) - 1); + } + } +} + +static bool init_clique_data_from_args(Obj digraph_obj, + Obj hook_obj, + Obj user_param_obj, + Obj limit_obj, + Obj include_obj, + Obj exclude_obj, + Obj max_obj, + Obj size_obj) { + static bool is_initialized = false; + if (!is_initialized) { + is_initialized = true; + + DIGRAPH = new_digraph(MAXVERTS); + + GRAPH = new_graph(MAXVERTS); + } + + uint16_t nr = DigraphNrVertices(digraph_obj); + + bool is_undirected; + if (CALL_1ARGS(IsSymmetricDigraph, digraph_obj) == True) { + init_clique_graph_from_digraph_obj(GRAPH, digraph_obj); + is_undirected = true; + } else { + init_clique_digraph_from_digraph_obj(DIGRAPH, digraph_obj); + is_undirected = false; + } + + if (hook_obj != Fail) { + GAP_FUNC = hook_obj; + HOOK = clique_hook_gap; + } else { + HOOK = clique_hook_collect; + } + USER_PARAM = user_param_obj; + + return true; +} + +Obj FuncDigraphsCliqueFinder(Obj self, Obj args) { + if (LEN_PLIST(args) != 8) { + ErrorQuit( + "there must be 8 arguments, found %d,", LEN_PLIST(args), 0L); + } + Obj digraph_obj = ELM_PLIST(args, 1); + Obj hook_obj = ELM_PLIST(args, 2); + Obj user_param_obj = ELM_PLIST(args, 3); + Obj limit_obj = ELM_PLIST(args, 4); + Obj include_obj = ELM_PLIST(args, 5); + Obj exclude_obj = ELM_PLIST(args, 6); + Obj max_obj = ELM_PLIST(args, 7); + Obj size_obj = ELM_PLIST(args, 8); + + // Validate the arguments + if (CALL_1ARGS(IsDigraph, digraph_obj) != True) { + ErrorQuit("the 1st argument must be a digraph, not %s,", + (Int) TNAM_OBJ(digraph_obj), + 0L); + } else if (DigraphNrVertices(digraph_obj) > MAXVERTS) { + ErrorQuit("the 1st argument must have at most 512 vertices, " + "found %d,", + DigraphNrVertices(digraph_obj), + 0L); + } + if (hook_obj == Fail) { + if (!IS_LIST(user_param_obj) || !IS_MUTABLE_OBJ(user_param_obj)) { + ErrorQuit("the 2rd argument is fail and so the 3th argument must " + "be a mutable list, not %s,", + (Int) TNAM_OBJ(user_param_obj), + 0L); + } + } else if (!IS_FUNC(hook_obj) || NARG_FUNC(hook_obj) != 2) { + ErrorQuit( + "the 2rd argument must be a function with 2 arguments,", 0L, 0L); + } + if (!IS_INTOBJ(limit_obj) && limit_obj != Fail) { + ErrorQuit("the 4th argument must be an integer " + "or fail, not %s,", + (Int) TNAM_OBJ(limit_obj), + 0L); + } else if (IS_INTOBJ(limit_obj) && INT_INTOBJ(limit_obj) <= 0) { + ErrorQuit("the 4th argument must be a positive integer, " + "not %d,", + INT_INTOBJ(limit_obj), + 0L); + } + if (max_obj != True && max_obj != False) { + ErrorQuit("the 7th argument must true or false, not %s,", + (Int) TNAM_OBJ(max_obj), + 0L); + } + if (!IS_INTOBJ(size_obj) && size_obj != Fail) { + ErrorQuit("the 8th argument must be an integer " + "or fail, not %s,", + (Int) TNAM_OBJ(size_obj), + 0L); + } else if (IS_INTOBJ(size_obj) && INT_INTOBJ(size_obj) <= 0) { + ErrorQuit("the 8th argument must be a positive integer, " + "not %d,", + INT_INTOBJ(size_obj), + 0L); + } + + // Initialise all the variable which will be used to carry out the recursion + if (!init_clique_data_from_args(digraph_obj, + hook_obj, + user_param_obj, + limit_obj, + include_obj, + exclude_obj, + max_obj, + size_obj)) { + return user_param_obj; + } + + + // go! + if (setjmp(OUTOFHERE) == 0) { + BronKerbosch(); + } + + return user_param_obj; +} + + diff --git a/src/cliques.h b/src/cliques.h new file mode 100644 index 000000000..c8aa5581d --- /dev/null +++ b/src/cliques.h @@ -0,0 +1,19 @@ +/******************************************************************************** +** +*A cliques.h di/cliques Julius Jonusas +** +** Copyright (C) 2020 - Julius Jonusas +** +** This file is free software, see the digraphs/LICENSE. +** +********************************************************************************/ + +#ifndef DIGRAPHS_SRC_CLIQUES_H_ +#define DIGRAPHS_SRC_CLIQUES_H_ + +// GAP headers +#include "src/compiled.h" + +Obj FuncDigraphsCliqueFinder(Obj self, Obj args); + +#endif // DIGRAPHS_SRC_CLIQUES_H_ diff --git a/src/digraphs.c b/src/digraphs.c index c5203c751..ac0394632 100644 --- a/src/digraphs.c +++ b/src/digraphs.c @@ -21,6 +21,7 @@ #include "digraphs-debug.h" // for DIGRAPHS_ASSERT #include "homos.h" // for FuncHomomorphismDigraphsFinder +#include "cliques.h" #include "planar.h" // for FUNC_IS_PLANAR, . . . #ifdef DIGRAPHS_WITH_INCLUDED_BLISS @@ -2250,6 +2251,13 @@ static StructGVarFunc GVarFuncs[] = { FuncHomomorphismDigraphsFinder, "src/homos.c:FuncHomomorphismDigraphsFinder"}, + {"DigraphsCliqueFinder", + -1, + "digraph, hook, user_param, limit, include, " + "exclude, max, size", + FuncDigraphsCliqueFinder, + "src/cliques.c:FuncDigraphsCliqueFinder"}, + {"IS_PLANAR", 1, "digraph", FuncIS_PLANAR, "src/planar.c:FuncIS_PLANAR"}, {"PLANAR_EMBEDDING", From b1d113c5473c3b13b7722cae7c5bff0709ffac5c Mon Sep 17 00:00:00 2001 From: Julius Date: Tue, 3 Mar 2020 16:46:33 +0100 Subject: [PATCH 02/30] add: implemented BronKerbosch with pivoting --- src/cliques.c | 78 ++++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 62 insertions(+), 16 deletions(-) diff --git a/src/cliques.c b/src/cliques.c index d7218f0c5..003349874 100644 --- a/src/cliques.c +++ b/src/cliques.c @@ -55,8 +55,8 @@ extern Obj LargestMovedPointPerms; static Obj GAP_FUNC; // Variable to hold a GAP level hook function static Obj (*HOOK)(void*, // HOOK function applied to every homo found - const uint16_t, - const uint8_t*); + const BitArray*, + const uint16_t); static void* USER_PARAM; // a USER_PARAM for the hook // Values in MAP are restricted to those positions in IMAGE_RESTRICT @@ -67,20 +67,51 @@ static Digraph* DIGRAPH; // Digraphs to hold incoming GAP digraphs static Graph* GRAPH; // Graphs to hold incoming GAP symmetric digraphs +static BitArray* CLIQUE; +static Conditions* TRY; +static Conditions* BAN; + //////////////////////////////////////////////////////////////////////////////// // //////////////////////////////////////////////////////////////////////////////// -static void BronKerbosch() { - - // TODO +static void BronKerbosch(uint16_t depth) { + + uint16_t nr = GRAPH->nr_vertices; + + if (size_bit_array(get_conditions(TRY, 0), nr) == 0 && size_bit_array(get_conditions(BAN,0), nr) ==0) { + // is a maximal clique + HOOK(USER_PARAM, CLIQUE, nr); + return; + } + + BitArray* try = get_conditions(TRY, 0); + for (uint16_t i = 0; i < nr; i++) { + if (get_bit_array(try, i)){ + set_bit_array(CLIQUE, i, true); + + + push_conditions(TRY, depth + 1, 0, GRAPH->neighbours[i]); + push_conditions(BAN, depth + 1, 0, GRAPH->neighbours[i]); + + // recurse + BronKerbosch(depth + 1); + + pop_conditions(TRY, depth + 1); + pop_conditions(BAN, depth + 1); + set_bit_array(CLIQUE, i, false); + + set_bit_array(get_conditions(TRY, 0), i , false); + set_bit_array(get_conditions(BAN, 0), i , true); + + } + } } -static Obj clique_hook_collect(void* user_param, uint16_t const nr, uint8_t const* clique) { - UInt2* ptr; +static Obj clique_hook_collect(void* user_param, const BitArray* clique, const uint16_t nr) { UInt i; Obj c; @@ -88,23 +119,26 @@ static Obj clique_hook_collect(void* user_param, uint16_t const nr, uint8_t cons RetypeBag(user_param, T_PLIST); } - c = NewEmptyPlist(); - for(i = 0; i < nr; i++) { - PushPlist(c, ObjInt_UInt8(clique[i])); + c = NEW_PLIST(T_PLIST, nr); + for(i = 1; i <= nr; i++) { + if (get_bit_array(clique, i - 1)){ + PushPlist(c, ObjInt_UInt(i)); + } } ASS_LIST(user_param, LEN_LIST(user_param) + 1, c); return False; } -static Obj clique_hook_gap(void* user_param, uint16_t const nr, uint8_t const* clique) { - UInt2* ptr; +static Obj clique_hook_gap(void* user_param, const BitArray* clique, const uint16_t nr) { UInt i; Obj c; - c = NewEmptyPlist(); - for(i = 0; i < nr; i++) { - PushPlist(c, ObjInt_UInt8(clique[i])); + c = NEW_PLIST(T_PLIST, nr); + for(i = 1; i <= nr; i++) { + if (get_bit_array(clique, i-1)) { + PushPlist(c, ObjInt_UInt(i + 1)); + } } return CALL_2ARGS(GAP_FUNC, user_param, c); @@ -167,9 +201,20 @@ static bool init_clique_data_from_args(Obj digraph_obj, DIGRAPH = new_digraph(MAXVERTS); GRAPH = new_graph(MAXVERTS); + + // Currently Conditions are a nr1 x nr1 array of BitArrays, so both + // values have to be set to MAXVERTS + CLIQUE = new_bit_array(MAXVERTS); + TRY = new_conditions(MAXVERTS, MAXVERTS); + BAN = new_conditions(MAXVERTS, MAXVERTS); } uint16_t nr = DigraphNrVertices(digraph_obj); + init_bit_array(CLIQUE, false, nr); + clear_conditions(TRY, nr + 1, nr); + clear_conditions(BAN, nr + 1, nr); + init_bit_array(BAN->bit_array[0], false, nr); + bool is_undirected; if (CALL_1ARGS(IsSymmetricDigraph, digraph_obj) == True) { @@ -269,8 +314,9 @@ Obj FuncDigraphsCliqueFinder(Obj self, Obj args) { // go! + if (setjmp(OUTOFHERE) == 0) { - BronKerbosch(); + BronKerbosch(0); } return user_param_obj; From 21e6ab6418b12b29cee4305aa0ef668838b4c540 Mon Sep 17 00:00:00 2001 From: Julius Date: Thu, 19 Mar 2020 15:40:32 +0100 Subject: [PATCH 03/30] add: BronKerboch now uses a pivot --- src/cliques.c | 31 +++++++++++++++++++++++++------ 1 file changed, 25 insertions(+), 6 deletions(-) diff --git a/src/cliques.c b/src/cliques.c index 003349874..28d9c0c5b 100644 --- a/src/cliques.c +++ b/src/cliques.c @@ -79,19 +79,40 @@ static Conditions* BAN; static void BronKerbosch(uint16_t depth) { uint16_t nr = GRAPH->nr_vertices; + BitArray* try = get_conditions(TRY, 0); + BitArray* ban = get_conditions(BAN, 0); - if (size_bit_array(get_conditions(TRY, 0), nr) == 0 && size_bit_array(get_conditions(BAN,0), nr) ==0) { + if (size_bit_array(try, nr) == 0 && size_bit_array(ban, nr) ==0) { // is a maximal clique HOOK(USER_PARAM, CLIQUE, nr); return; } - BitArray* try = get_conditions(TRY, 0); + // Choose a pivot with as a many neighbours in as possible + uint16_t pivot = 0; + int max_neighbours = -1; + for (uint16_t i = 0; i < nr; i++){ + if (get_bit_array(try, i) || get_bit_array(ban, i)){ + BitArray* copy_try = new_bit_array(MAXVERTS); // reuse it! + copy_bit_array(copy_try, try, nr); + intersect_bit_arrays(copy_try, GRAPH->neighbours[i], nr); + uint16_t num_neighbours = size_bit_array(copy_try, nr); + if (num_neighbours > max_neighbours) { + pivot = i; + max_neighbours = num_neighbours; + } + } + } + + // Try adding vertices from minus neighbours of + BitArray* to_try = new_bit_array(MAXVERTS); + init_bit_array(to_try, 1, nr); + complement_bit_arrays(to_try, GRAPH->neighbours[pivot], nr); + intersect_bit_arrays(to_try, try, nr); for (uint16_t i = 0; i < nr; i++) { - if (get_bit_array(try, i)){ + if (get_bit_array(to_try, i)){ set_bit_array(CLIQUE, i, true); - push_conditions(TRY, depth + 1, 0, GRAPH->neighbours[i]); push_conditions(BAN, depth + 1, 0, GRAPH->neighbours[i]); @@ -103,9 +124,7 @@ static void BronKerbosch(uint16_t depth) { set_bit_array(CLIQUE, i, false); set_bit_array(get_conditions(TRY, 0), i , false); - set_bit_array(get_conditions(BAN, 0), i , true); - } } } From 4ceb7d402a2adbb7e77721dddea237224447506d Mon Sep 17 00:00:00 2001 From: Julius Date: Thu, 19 Mar 2020 17:32:33 +0100 Subject: [PATCH 04/30] add: only use the symmetric part of the graph in the search cliques --- src/cliques.c | 52 +++++++++++++-------------------------------------- 1 file changed, 13 insertions(+), 39 deletions(-) diff --git a/src/cliques.c b/src/cliques.c index 28d9c0c5b..198114d1d 100644 --- a/src/cliques.c +++ b/src/cliques.c @@ -36,6 +36,7 @@ // Defined in digraphs.h Int DigraphNrVertices(Obj); Obj FuncOutNeighbours(Obj, Obj); +Obj FuncADJACENCY_MATRIX(Obj, Obj); // GAP level things, imported in digraphs.c extern Obj IsDigraph; @@ -62,9 +63,6 @@ static void* USER_PARAM; // a USER_PARAM for the hook // Values in MAP are restricted to those positions in IMAGE_RESTRICT static jmp_buf OUTOFHERE; // so we can jump out of the deepest - -static Digraph* DIGRAPH; // Digraphs to hold incoming GAP digraphs - static Graph* GRAPH; // Graphs to hold incoming GAP symmetric digraphs static BitArray* CLIQUE; @@ -168,39 +166,23 @@ static void init_clique_graph_from_digraph_obj(Graph* const graph, Obj digraph_obj) { DIGRAPHS_ASSERT(graph != NULL); DIGRAPHS_ASSERT(CALL_1ARGS(IsDigraph, digraph_obj) == True); - DIGRAPHS_ASSERT(CALL_1ARGS(IsSymmetricDigraph, digraph_obj) == True); UInt const nr = DigraphNrVertices(digraph_obj); Obj out = FuncOutNeighbours(0L, digraph_obj); + Obj adj_mat = FuncADJACENCY_MATRIX(0L, digraph_obj); DIGRAPHS_ASSERT(nr < MAXVERTS); DIGRAPHS_ASSERT(IS_PLIST(out)); clear_graph(graph, nr); - for (uint16_t i = 1; i <= nr; i++) { - Obj nbs = ELM_PLIST(out, i); - DIGRAPHS_ASSERT(IS_LIST(nbs)); - for (uint16_t j = 1; j <= LEN_LIST(nbs); j++) { - DIGRAPHS_ASSERT(IS_INTOBJ(ELM_LIST(nbs, j))); - add_edge_graph(graph, i - 1, INT_INTOBJ(ELM_LIST(nbs, j)) - 1); - } - } -} - -static void init_clique_digraph_from_digraph_obj(Digraph* const digraph, - Obj digraph_obj) { - DIGRAPHS_ASSERT(digraph != NULL); - DIGRAPHS_ASSERT(CALL_1ARGS(IsDigraph, digraph_obj) == True); - UInt const nr = DigraphNrVertices(digraph_obj); - Obj out = FuncOutNeighbours(0L, digraph_obj); - DIGRAPHS_ASSERT(nr < MAXVERTS); - DIGRAPHS_ASSERT(IS_PLIST(out)); - clear_digraph(digraph, nr); + + // Only include symmetric edges for (uint16_t i = 1; i <= nr; i++) { - Obj nbs = ELM_PLIST(out, i); - DIGRAPHS_ASSERT(IS_LIST(nbs)); - for (uint16_t j = 1; j <= LEN_LIST(nbs); j++) { - DIGRAPHS_ASSERT(IS_INTOBJ(ELM_LIST(nbs, j))); - add_edge_digraph(digraph, i - 1, INT_INTOBJ(ELM_LIST(nbs, j)) - 1); + Obj row = ELM_PLIST(adj_mat, i); + DIGRAPHS_ASSERT(IS_LIST(row)); + for (uint16_t j = 1; j <= nr; j++) { + if (ELM_PLIST(row, j) != INTOBJ_INT(0) && ELM_PLIST(ELM_PLIST(adj_mat,j) ,i) != INTOBJ_INT(0)){ + add_edge_graph(graph, i - 1, j - 1); + } } } } @@ -217,8 +199,6 @@ static bool init_clique_data_from_args(Obj digraph_obj, if (!is_initialized) { is_initialized = true; - DIGRAPH = new_digraph(MAXVERTS); - GRAPH = new_graph(MAXVERTS); // Currently Conditions are a nr1 x nr1 array of BitArrays, so both @@ -235,14 +215,8 @@ static bool init_clique_data_from_args(Obj digraph_obj, init_bit_array(BAN->bit_array[0], false, nr); - bool is_undirected; - if (CALL_1ARGS(IsSymmetricDigraph, digraph_obj) == True) { - init_clique_graph_from_digraph_obj(GRAPH, digraph_obj); - is_undirected = true; - } else { - init_clique_digraph_from_digraph_obj(DIGRAPH, digraph_obj); - is_undirected = false; - } + // TODO: deal with non-symmetricity + init_clique_graph_from_digraph_obj(GRAPH, digraph_obj); if (hook_obj != Fail) { GAP_FUNC = hook_obj; @@ -269,7 +243,7 @@ Obj FuncDigraphsCliqueFinder(Obj self, Obj args) { Obj max_obj = ELM_PLIST(args, 7); Obj size_obj = ELM_PLIST(args, 8); - // Validate the arguments + // Validate the arguments if (CALL_1ARGS(IsDigraph, digraph_obj) != True) { ErrorQuit("the 1st argument must be a digraph, not %s,", (Int) TNAM_OBJ(digraph_obj), From 754db7c5217b5266a9f8c686a14dda96f8ec3d12 Mon Sep 17 00:00:00 2001 From: Julius Date: Wed, 25 Mar 2020 15:50:34 +0100 Subject: [PATCH 05/30] add: implemented the use of the addional parameters -- limit, max, size. --- src/cliques.c | 86 ++++++++++++++++++++++++++++++++------------------- 1 file changed, 54 insertions(+), 32 deletions(-) diff --git a/src/cliques.c b/src/cliques.c index 198114d1d..ad73af013 100644 --- a/src/cliques.c +++ b/src/cliques.c @@ -69,44 +69,66 @@ static BitArray* CLIQUE; static Conditions* TRY; static Conditions* BAN; - //////////////////////////////////////////////////////////////////////////////// // //////////////////////////////////////////////////////////////////////////////// -static void BronKerbosch(uint16_t depth) { +static void BronKerbosch(uint16_t depth, + uint16_t limit, + uint16_t* nr_found, + bool max, + uint16_t size) { uint16_t nr = GRAPH->nr_vertices; BitArray* try = get_conditions(TRY, 0); BitArray* ban = get_conditions(BAN, 0); - if (size_bit_array(try, nr) == 0 && size_bit_array(ban, nr) ==0) { - // is a maximal clique + if (depth > 0 && !max && ( size == 0 || size == depth)) { + // We are not looking for maximal cliques HOOK(USER_PARAM, CLIQUE, nr); + *nr_found += 1; + if (*nr_found >= limit) { + longjmp(OUTOFHERE, 1); + } + } else if (size_bit_array(try, nr) == 0 && size_bit_array(ban, nr) == 0) { + // is a maximal clique + if (size == 0 || size == depth) { + HOOK(USER_PARAM, CLIQUE, nr); + *nr_found += 1; + if (*nr_found >= limit) { + longjmp(OUTOFHERE, 1); + } + } return; } - // Choose a pivot with as a many neighbours in as possible - uint16_t pivot = 0; - int max_neighbours = -1; - for (uint16_t i = 0; i < nr; i++){ - if (get_bit_array(try, i) || get_bit_array(ban, i)){ - BitArray* copy_try = new_bit_array(MAXVERTS); // reuse it! - copy_bit_array(copy_try, try, nr); - intersect_bit_arrays(copy_try, GRAPH->neighbours[i], nr); - uint16_t num_neighbours = size_bit_array(copy_try, nr); - if (num_neighbours > max_neighbours) { - pivot = i; - max_neighbours = num_neighbours; + BitArray* to_try = new_bit_array(MAXVERTS); + if (max) { + // Choose a pivot with as a many neighbours in as possible + uint16_t pivot = 0; + int max_neighbours = -1; + for (uint16_t i = 0; i < nr; i++){ + if (get_bit_array(try, i) || get_bit_array(ban, i)){ + BitArray* copy_try = new_bit_array(MAXVERTS); // reuse it! + copy_bit_array(copy_try, try, nr); + intersect_bit_arrays(copy_try, GRAPH->neighbours[i], nr); + uint16_t num_neighbours = size_bit_array(copy_try, nr); + if (num_neighbours > max_neighbours) { + pivot = i; + max_neighbours = num_neighbours; + } } } + + // Try adding vertices from minus neighbours of + init_bit_array(to_try, 1, nr); + complement_bit_arrays(to_try, GRAPH->neighbours[pivot], nr); + intersect_bit_arrays(to_try, try, nr); + } else { + // If we are not looking for maximal cliques, a pivot cannot be used + copy_bit_array(to_try, try, nr); } - // Try adding vertices from minus neighbours of - BitArray* to_try = new_bit_array(MAXVERTS); - init_bit_array(to_try, 1, nr); - complement_bit_arrays(to_try, GRAPH->neighbours[pivot], nr); - intersect_bit_arrays(to_try, try, nr); for (uint16_t i = 0; i < nr; i++) { if (get_bit_array(to_try, i)){ set_bit_array(CLIQUE, i, true); @@ -115,7 +137,7 @@ static void BronKerbosch(uint16_t depth) { push_conditions(BAN, depth + 1, 0, GRAPH->neighbours[i]); // recurse - BronKerbosch(depth + 1); + BronKerbosch(depth + 1, limit, nr_found, max, size); pop_conditions(TRY, depth + 1); pop_conditions(BAN, depth + 1); @@ -190,7 +212,6 @@ static void init_clique_graph_from_digraph_obj(Graph* const graph, static bool init_clique_data_from_args(Obj digraph_obj, Obj hook_obj, Obj user_param_obj, - Obj limit_obj, Obj include_obj, Obj exclude_obj, Obj max_obj, @@ -214,8 +235,6 @@ static bool init_clique_data_from_args(Obj digraph_obj, clear_conditions(BAN, nr + 1, nr); init_bit_array(BAN->bit_array[0], false, nr); - - // TODO: deal with non-symmetricity init_clique_graph_from_digraph_obj(GRAPH, digraph_obj); if (hook_obj != Fail) { @@ -265,9 +284,9 @@ Obj FuncDigraphsCliqueFinder(Obj self, Obj args) { ErrorQuit( "the 2rd argument must be a function with 2 arguments,", 0L, 0L); } - if (!IS_INTOBJ(limit_obj) && limit_obj != Fail) { + if (!IS_INTOBJ(limit_obj) && limit_obj != Infinity) { ErrorQuit("the 4th argument must be an integer " - "or fail, not %s,", + "or infinity, not %s,", (Int) TNAM_OBJ(limit_obj), 0L); } else if (IS_INTOBJ(limit_obj) && INT_INTOBJ(limit_obj) <= 0) { @@ -297,7 +316,6 @@ Obj FuncDigraphsCliqueFinder(Obj self, Obj args) { if (!init_clique_data_from_args(digraph_obj, hook_obj, user_param_obj, - limit_obj, include_obj, exclude_obj, max_obj, @@ -305,14 +323,18 @@ Obj FuncDigraphsCliqueFinder(Obj self, Obj args) { return user_param_obj; } + uint16_t nr_found = 0; + uint16_t limit = (limit_obj == Infinity ? MAXVERTS : INT_INTOBJ(limit_obj)); + uint16_t size = (size_obj == Fail ? 0 : INT_INTOBJ(size_obj)); + bool max = (max_obj == True ? true : false); + if (size > GRAPH->nr_vertices) { + return user_param_obj; + } // go! - if (setjmp(OUTOFHERE) == 0) { - BronKerbosch(0); + BronKerbosch(0, limit, &nr_found, max, size); } return user_param_obj; } - - From a95a2c7e742b33b3003fa53a4b9dd7e304b3ce77 Mon Sep 17 00:00:00 2001 From: Julius Date: Fri, 3 Apr 2020 16:32:21 +0200 Subject: [PATCH 06/30] add: using the automorphism group in the computation --- src/bitarray.h | 17 ++++++ src/cliques.c | 161 +++++++++++++++++++++++++++++++++++++++++++------ src/homos.c | 4 +- 3 files changed, 162 insertions(+), 20 deletions(-) diff --git a/src/bitarray.h b/src/bitarray.h index 0cd7c0e40..ac7673ffb 100644 --- a/src/bitarray.h +++ b/src/bitarray.h @@ -339,6 +339,23 @@ static inline void intersect_bit_arrays(BitArray* const bit_array1, } } +//! Union the BitArray's pointed to by \p bit_array1 and \p bit_array2. The +//! BitArray pointed to by \p bit_array1 is changed in place! +static inline void union_bit_arrays(BitArray* const bit_array1, + BitArray const* const bit_array2, + uint16_t const nr_bits) { + DIGRAPHS_ASSERT(bit_array1 != NULL); + DIGRAPHS_ASSERT(bit_array2 != NULL); + DIGRAPHS_ASSERT(bit_array1->nr_bits == bit_array2->nr_bits); + DIGRAPHS_ASSERT(bit_array1->nr_blocks == bit_array2->nr_blocks); + DIGRAPHS_ASSERT(nr_bits <= bit_array1->nr_bits); + DIGRAPHS_ASSERT(nr_bits <= bit_array2->nr_bits); + uint16_t const nr_blocks = NR_BLOCKS_LOOKUP[nr_bits]; + for (uint16_t i = 0; i < nr_blocks; i++) { + bit_array1->blocks[i] |= bit_array2->blocks[i]; + } +} + //! Sets \p bit_array1 to be 0 in every position that \p bit_array2 is 1. static inline void complement_bit_arrays(BitArray* const bit_array1, BitArray const* const bit_array2, diff --git a/src/cliques.c b/src/cliques.c index ad73af013..25746d909 100644 --- a/src/cliques.c +++ b/src/cliques.c @@ -38,6 +38,10 @@ Int DigraphNrVertices(Obj); Obj FuncOutNeighbours(Obj, Obj); Obj FuncADJACENCY_MATRIX(Obj, Obj); +// Defined in homos.c +void set_automorphisms(Obj, PermColl*); +void get_automorphism_group_from_gap(Obj, PermColl*); + // GAP level things, imported in digraphs.c extern Obj IsDigraph; extern Obj DIGRAPHS_ValidateVertexColouring; @@ -69,11 +73,52 @@ static BitArray* CLIQUE; static Conditions* TRY; static Conditions* BAN; +static uint16_t ORB[MAXVERTS]; // Array for containing nodes in an orbit. +static BitArray* ORB_LOOKUP; // points in orbit +static PermColl* STAB_GENS[MAXVERTS]; // stabiliser generators +static SchreierSims* SCHREIER_SIMS; + //////////////////////////////////////////////////////////////////////////////// // //////////////////////////////////////////////////////////////////////////////// +// Update a BitArray to only include one vertex per orbit of a group generated +// by STAB_GENS[rep_depth] +static void get_orbit_reps_bitarray(BitArray* bit_array, uint16_t const rep_depth) { + if (STAB_GENS[rep_depth]->size == 0) { + return; + } + + uint16_t nr = GRAPH->nr_vertices; + uint16_t pt = 0; + init_bit_array(ORB_LOOKUP, false, nr); + while (pt < nr) { + if (get_bit_array(bit_array, pt)) { + // Find the orbit of pt and remove all other points of the orbit from bit_array + + set_bit_array(ORB_LOOKUP, pt, true); + ORB[0] = pt; + uint16_t n = 1; // lenght of the orbit of pt + + for (uint16_t i = 0; i < n; ++i) { + for (uint16_t j = 0; j < STAB_GENS[rep_depth]->size; ++j) { + Perm gen = STAB_GENS[rep_depth]->perms[j]; + uint16_t const img = gen[ORB[i]]; + if (!get_bit_array(ORB_LOOKUP, img)) { + ORB[n++] = img; + set_bit_array(ORB_LOOKUP, img, true); + set_bit_array(bit_array, img, false); + } + } + } + } + pt++; + } +} + + static void BronKerbosch(uint16_t depth, + uint16_t rep_depth, uint16_t limit, uint16_t* nr_found, bool max, @@ -129,23 +174,54 @@ static void BronKerbosch(uint16_t depth, copy_bit_array(to_try, try, nr); } - for (uint16_t i = 0; i < nr; i++) { - if (get_bit_array(to_try, i)){ - set_bit_array(CLIQUE, i, true); + // Get orbit representatives of + get_orbit_reps_bitarray(to_try, rep_depth); - push_conditions(TRY, depth + 1, 0, GRAPH->neighbours[i]); - push_conditions(BAN, depth + 1, 0, GRAPH->neighbours[i]); + for (uint16_t pt = 0; pt < nr; pt++) { + if (get_bit_array(to_try, pt)){ + set_bit_array(CLIQUE, pt, true); + + push_conditions(TRY, depth + 1, 0, GRAPH->neighbours[pt]); + push_conditions(BAN, depth + 1, 0, GRAPH->neighbours[pt]); // recurse - BronKerbosch(depth + 1, limit, nr_found, max, size); + if (STAB_GENS[rep_depth]->size == 0) { + BronKerbosch(depth + 1, rep_depth, limit, nr_found, max, size); + } else { + // the point_stabilizer is very SLOW! + point_stabilizer(SCHREIER_SIMS, STAB_GENS[rep_depth], + STAB_GENS[rep_depth + 1], pt); + BronKerbosch(depth + 1, rep_depth + 1, limit, nr_found, max, size); + } pop_conditions(TRY, depth + 1); pop_conditions(BAN, depth + 1); - set_bit_array(CLIQUE, i, false); - - set_bit_array(get_conditions(TRY, 0), i , false); - set_bit_array(get_conditions(BAN, 0), i , true); + set_bit_array(CLIQUE, pt, false); + + // if (STAB_GENS[rep_depth]->size == 0) { + set_bit_array(get_conditions(TRY, 0), pt, false); + set_bit_array(get_conditions(BAN, 0), pt, true); + // } else { + // init_bit_array(ORB_LOOKUP, false, nr); + // set_bit_array(ORB_LOOKUP, pt, true); + // ORB[0] = pt; + // uint16_t n = 1; // lenght of the orbit of pt + + // for (uint16_t i = 0; i < n; ++i) { + // for (uint16_t j = 0; j < STAB_GENS[rep_depth]->size; ++j) { + // Perm gen = STAB_GENS[rep_depth]->perms[j]; + // uint16_t const img = gen[ORB[i]]; + // if (!get_bit_array(ORB_LOOKUP, img)) { + // ORB[n++] = img; + // set_bit_array(ORB_LOOKUP, img, true); + // } + // } + // } + // complement_bit_arrays(get_conditions(TRY,0), ORB_LOOKUP, nr); + // union_bit_arrays(get_conditions(BAN,0), ORB_LOOKUP, nr); + // } } + } } @@ -183,7 +259,6 @@ static Obj clique_hook_gap(void* user_param, const BitArray* clique, const uint1 return CALL_2ARGS(GAP_FUNC, user_param, c); } - static void init_clique_graph_from_digraph_obj(Graph* const graph, Obj digraph_obj) { DIGRAPHS_ASSERT(graph != NULL); @@ -195,8 +270,6 @@ static void init_clique_graph_from_digraph_obj(Graph* const graph, DIGRAPHS_ASSERT(IS_PLIST(out)); clear_graph(graph, nr); - - // Only include symmetric edges for (uint16_t i = 1; i <= nr; i++) { Obj row = ELM_PLIST(adj_mat, i); @@ -215,7 +288,8 @@ static bool init_clique_data_from_args(Obj digraph_obj, Obj include_obj, Obj exclude_obj, Obj max_obj, - Obj size_obj) { + Obj size_obj, + Obj aut_grp_obj) { static bool is_initialized = false; if (!is_initialized) { is_initialized = true; @@ -227,6 +301,12 @@ static bool init_clique_data_from_args(Obj digraph_obj, CLIQUE = new_bit_array(MAXVERTS); TRY = new_conditions(MAXVERTS, MAXVERTS); BAN = new_conditions(MAXVERTS, MAXVERTS); + + ORB_LOOKUP = new_bit_array(MAXVERTS); + for (uint16_t i = 0; i < MAXVERTS; i++) { + STAB_GENS[i] = new_perm_coll(MAXVERTS, MAXVERTS); + } + SCHREIER_SIMS = new_schreier_sims(); } uint16_t nr = DigraphNrVertices(digraph_obj); @@ -245,13 +325,23 @@ static bool init_clique_data_from_args(Obj digraph_obj, } USER_PARAM = user_param_obj; + + // Get generators of the automorphism group the graph + PERM_DEGREE = nr; + if (aut_grp_obj == Fail) { + // TODO: should we use BLISS instead? Otherwise drop digraph directions in GAP + get_automorphism_group_from_gap(digraph_obj, STAB_GENS[0]); + } else { + set_automorphisms(aut_grp_obj, STAB_GENS[0]); + } + return true; } Obj FuncDigraphsCliqueFinder(Obj self, Obj args) { - if (LEN_PLIST(args) != 8) { + if (LEN_PLIST(args) != 8 && LEN_PLIST(args) != 9) { ErrorQuit( - "there must be 8 arguments, found %d,", LEN_PLIST(args), 0L); + "there must be 8 or 9 arguments, found %d,", LEN_PLIST(args), 0L); } Obj digraph_obj = ELM_PLIST(args, 1); Obj hook_obj = ELM_PLIST(args, 2); @@ -261,6 +351,11 @@ Obj FuncDigraphsCliqueFinder(Obj self, Obj args) { Obj exclude_obj = ELM_PLIST(args, 6); Obj max_obj = ELM_PLIST(args, 7); Obj size_obj = ELM_PLIST(args, 8); + Obj aut_grp_obj = Fail; + if (LEN_PLIST(args) == 9) { + aut_grp_obj = ELM_PLIST(args, 9); + } + // Validate the arguments if (CALL_1ARGS(IsDigraph, digraph_obj) != True) { @@ -312,6 +407,35 @@ Obj FuncDigraphsCliqueFinder(Obj self, Obj args) { 0L); } + if (aut_grp_obj != Fail) { + if (CALL_1ARGS(IsPermGroup, aut_grp_obj) != True) { + ErrorQuit( + "the 9th argument must be a permutation group " + "or fail, not %s,", + (Int) TNAM_OBJ(aut_grp_obj), + 0L); + } + Obj gens = CALL_1ARGS(GeneratorsOfGroup, aut_grp_obj); + DIGRAPHS_ASSERT(IS_LIST(gens)); + DIGRAPHS_ASSERT(LEN_LIST(gens) > 0); + UInt lmp = INT_INTOBJ(CALL_1ARGS(LargestMovedPointPerms, gens)); + if (lmp > 0 && LEN_LIST(gens) >= lmp) { + ErrorQuit("expected at most %d generators in the 9th argument " + "but got %d,", + lmp - 1, + LEN_LIST(gens)); + } + for (UInt i = 1; i <= LEN_LIST(gens); ++i) { + if (CALL_2ARGS(IsDigraphAutomorphism, digraph_obj, ELM_LIST(gens, i)) + != True) { + ErrorQuit("expected group of automorphisms, but found a " + "non-automorphism in position %d of the group generators,", + i, + 0L); + } + } + } + // Initialise all the variable which will be used to carry out the recursion if (!init_clique_data_from_args(digraph_obj, hook_obj, @@ -319,7 +443,8 @@ Obj FuncDigraphsCliqueFinder(Obj self, Obj args) { include_obj, exclude_obj, max_obj, - size_obj)) { + size_obj, + aut_grp_obj)) { return user_param_obj; } @@ -333,7 +458,7 @@ Obj FuncDigraphsCliqueFinder(Obj self, Obj args) { // go! if (setjmp(OUTOFHERE) == 0) { - BronKerbosch(0, limit, &nr_found, max, size); + BronKerbosch(0, 0, limit, &nr_found, max, size); } return user_param_obj; diff --git a/src/homos.c b/src/homos.c index 52f6eba7f..3ec8e8d23 100644 --- a/src/homos.c +++ b/src/homos.c @@ -294,7 +294,7 @@ homo_hook_collect(void* user_param, uint16_t const nr, uint16_t const* map) { // printf(" }>"); // } -static void get_automorphism_group_from_gap(Obj digraph_obj, PermColl* out) { +void get_automorphism_group_from_gap(Obj digraph_obj, PermColl* out) { DIGRAPHS_ASSERT(CALL_1ARGS(IsDigraph, digraph_obj) == True); Obj o = CALL_1ARGS(AutomorphismGroup, digraph_obj); o = CALL_1ARGS(GeneratorsOfGroup, o); @@ -523,7 +523,7 @@ static void internal_order_map_graph(Graph const* const graph) { } } -static void set_automorphisms(Obj aut_grp_obj, PermColl* out) { +void set_automorphisms(Obj aut_grp_obj, PermColl* out) { DIGRAPHS_ASSERT(out != NULL); clear_perm_coll(out); out->degree = PERM_DEGREE; From c3643802f1055d6cf191177ba5f95e263ed391bb Mon Sep 17 00:00:00 2001 From: Julius Date: Mon, 6 Apr 2020 18:17:31 +0200 Subject: [PATCH 07/30] add: now uses include parameter correctly --- src/cliques.c | 167 +++++++++++++++++++++++++++++++++++++------------ src/digraphs.c | 2 + 2 files changed, 130 insertions(+), 39 deletions(-) diff --git a/src/cliques.c b/src/cliques.c index 25746d909..9a4ef79db 100644 --- a/src/cliques.c +++ b/src/cliques.c @@ -52,6 +52,7 @@ extern Obj AutomorphismGroup; extern Obj IsPermGroup; extern Obj IsDigraphAutomorphism; extern Obj LargestMovedPointPerms; +extern Obj IsClique; //////////////////////////////////////////////////////////////////////////////// // Global variables @@ -149,7 +150,7 @@ static void BronKerbosch(uint16_t depth, BitArray* to_try = new_bit_array(MAXVERTS); if (max) { - // Choose a pivot with as a many neighbours in as possible + // Choose a pivot with as many neighbours in as possible uint16_t pivot = 0; int max_neighbours = -1; for (uint16_t i = 0; i < nr; i++){ @@ -198,28 +199,28 @@ static void BronKerbosch(uint16_t depth, pop_conditions(BAN, depth + 1); set_bit_array(CLIQUE, pt, false); - // if (STAB_GENS[rep_depth]->size == 0) { + if (STAB_GENS[rep_depth]->size == 0) { set_bit_array(get_conditions(TRY, 0), pt, false); set_bit_array(get_conditions(BAN, 0), pt, true); - // } else { - // init_bit_array(ORB_LOOKUP, false, nr); - // set_bit_array(ORB_LOOKUP, pt, true); - // ORB[0] = pt; - // uint16_t n = 1; // lenght of the orbit of pt - - // for (uint16_t i = 0; i < n; ++i) { - // for (uint16_t j = 0; j < STAB_GENS[rep_depth]->size; ++j) { - // Perm gen = STAB_GENS[rep_depth]->perms[j]; - // uint16_t const img = gen[ORB[i]]; - // if (!get_bit_array(ORB_LOOKUP, img)) { - // ORB[n++] = img; - // set_bit_array(ORB_LOOKUP, img, true); - // } - // } - // } - // complement_bit_arrays(get_conditions(TRY,0), ORB_LOOKUP, nr); - // union_bit_arrays(get_conditions(BAN,0), ORB_LOOKUP, nr); - // } + } else { + init_bit_array(ORB_LOOKUP, false, nr); + set_bit_array(ORB_LOOKUP, pt, true); + ORB[0] = pt; + uint16_t n = 1; // lenght of the orbit of pt + + for (uint16_t i = 0; i < n; ++i) { + for (uint16_t j = 0; j < STAB_GENS[rep_depth]->size; ++j) { + Perm gen = STAB_GENS[rep_depth]->perms[j]; + uint16_t const img = gen[ORB[i]]; + if (!get_bit_array(ORB_LOOKUP, img)) { + ORB[n++] = img; + set_bit_array(ORB_LOOKUP, img, true); + } + } + } + complement_bit_arrays(get_conditions(TRY,0), ORB_LOOKUP, nr); + union_bit_arrays(get_conditions(BAN,0), ORB_LOOKUP, nr); + } } } @@ -288,7 +289,6 @@ static bool init_clique_data_from_args(Obj digraph_obj, Obj include_obj, Obj exclude_obj, Obj max_obj, - Obj size_obj, Obj aut_grp_obj) { static bool is_initialized = false; if (!is_initialized) { @@ -309,13 +309,25 @@ static bool init_clique_data_from_args(Obj digraph_obj, SCHREIER_SIMS = new_schreier_sims(); } + init_clique_graph_from_digraph_obj(GRAPH, digraph_obj); + uint16_t nr = DigraphNrVertices(digraph_obj); - init_bit_array(CLIQUE, false, nr); clear_conditions(TRY, nr + 1, nr); clear_conditions(BAN, nr + 1, nr); init_bit_array(BAN->bit_array[0], false, nr); - init_clique_graph_from_digraph_obj(GRAPH, digraph_obj); + // Update and CLIQUE and TRY using include_obj + if (include_obj != Fail) { + init_bit_array(CLIQUE, false, nr); + set_bit_array_from_gap_list(CLIQUE, include_obj); + complement_bit_arrays(get_conditions(TRY, 0), CLIQUE, nr); + for (uint16_t i = 1; i <= LEN_LIST(include_obj); ++i) { + intersect_bit_arrays(get_conditions(TRY, 0), + GRAPH->neighbours[INT_INTOBJ(ELM_LIST(include_obj, i)) - 1], + nr); + } + } + if (hook_obj != Fail) { GAP_FUNC = hook_obj; @@ -356,7 +368,6 @@ Obj FuncDigraphsCliqueFinder(Obj self, Obj args) { aut_grp_obj = ELM_PLIST(args, 9); } - // Validate the arguments if (CALL_1ARGS(IsDigraph, digraph_obj) != True) { ErrorQuit("the 1st argument must be a digraph, not %s,", @@ -380,28 +391,80 @@ Obj FuncDigraphsCliqueFinder(Obj self, Obj args) { "the 2rd argument must be a function with 2 arguments,", 0L, 0L); } if (!IS_INTOBJ(limit_obj) && limit_obj != Infinity) { - ErrorQuit("the 4th argument must be an integer " + ErrorQuit("the 4th argument must be an integer " "or infinity, not %s,", (Int) TNAM_OBJ(limit_obj), 0L); } else if (IS_INTOBJ(limit_obj) && INT_INTOBJ(limit_obj) <= 0) { - ErrorQuit("the 4th argument must be a positive integer, " + ErrorQuit("the 4th argument must be a positive integer, " "not %d,", INT_INTOBJ(limit_obj), 0L); } + if (!IS_LIST(include_obj) && include_obj != Fail) { + ErrorQuit("the 5th argument must be a list or fail, not %s,", + (Int) TNAM_OBJ(include_obj), + 0L); + } else if (IS_LIST(include_obj)) { + for (Int i = 1; i <= LEN_LIST(include_obj); ++i) { + if (!ISB_LIST(include_obj, i)) { + ErrorQuit("the 5th argument must be a dense list,", 0L, 0L); + } else if (!IS_POS_INT(ELM_LIST(include_obj, i))) { + ErrorQuit("the 5th argument must only contain positive " + "integers, but found %s in position %d,", + (Int) TNAM_OBJ(ELM_LIST(include_obj, i)), + i); + } else if (INT_INTOBJ(ELM_LIST(include_obj, i)) + > DigraphNrVertices(digraph_obj)) { + ErrorQuit("in the 5th argument position %d is out of range, " + "must be in the range [1, %d],", + i, + DigraphNrVertices(digraph_obj)); + } else if (INT_INTOBJ( + POS_LIST(include_obj, ELM_LIST(include_obj, i), INTOBJ_INT(0))) + < i) { + ErrorQuit("in the 5th argument position %d is a duplicate,", i, 0L); + } + } + } + if (!IS_LIST(exclude_obj) && exclude_obj != Fail) { + ErrorQuit("the 6th argument must be a list or fail, not %s,", + (Int) TNAM_OBJ(exclude_obj), + 0L); + } else if (IS_LIST(exclude_obj)) { + for (Int i = 1; i <= LEN_LIST(exclude_obj); ++i) { + if (!ISB_LIST(exclude_obj, i)) { + ErrorQuit("the 6th argument must be a dense list,", 0L, 0L); + } else if (!IS_POS_INT(ELM_LIST(exclude_obj, i))) { + ErrorQuit("the 6th argument must only contain positive " + "integers, but found %s in position %d,", + (Int) TNAM_OBJ(ELM_LIST(exclude_obj, i)), + i); + } else if (INT_INTOBJ(ELM_LIST(exclude_obj, i)) + > DigraphNrVertices(digraph_obj)) { + ErrorQuit("in the 6th argument position %d is out of range, " + "must be in the range [1, %d],", + i, + DigraphNrVertices(digraph_obj)); + } else if (INT_INTOBJ( + POS_LIST(exclude_obj, ELM_LIST(exclude_obj, i), INTOBJ_INT(0))) + < i) { + ErrorQuit("in the 6th argument position %d is a duplicate,", i, 0L); + } + } + } if (max_obj != True && max_obj != False) { - ErrorQuit("the 7th argument must true or false, not %s,", + ErrorQuit("the 7th argument must true or false, not %s,", (Int) TNAM_OBJ(max_obj), 0L); } if (!IS_INTOBJ(size_obj) && size_obj != Fail) { - ErrorQuit("the 8th argument must be an integer " + ErrorQuit("the 8th argument must be an integer " "or fail, not %s,", (Int) TNAM_OBJ(size_obj), 0L); } else if (IS_INTOBJ(size_obj) && INT_INTOBJ(size_obj) <= 0) { - ErrorQuit("the 8th argument must be a positive integer, " + ErrorQuit("the 8th argument must be a positive integer, " "not %d,", INT_INTOBJ(size_obj), 0L); @@ -436,6 +499,40 @@ Obj FuncDigraphsCliqueFinder(Obj self, Obj args) { } } + uint16_t size = (size_obj == Fail ? 0 : INT_INTOBJ(size_obj)); + uint16_t include_size = (include_obj == Fail ? 0 : LEN_LIST(include_obj)); + uint16_t exclude_size = (exclude_obj == Fail ? 0 : LEN_LIST(exclude_obj)); + uint16_t nr = DigraphNrVertices(digraph_obj); + + // Check the trivial cases: + // The desired clique is too small + if (size != 0 && include_size > size) { + return user_param_obj; + } + // The desired clique is too big + if (size != 0 && size > nr - exclude_size) { + return user_param_obj; + } + // Check if include and exclude have empty intersection + if (include_size != 0 && exclude_size != 0) { + bool lookup[MAXVERTS] = {false}; + for (uint16_t i = 0; i < include_size; ++i) { + lookup[INT_INTOBJ(ELM_LIST(include_obj, i)) - 1] = true; + } + for (uint16_t i = 0; i < exclude_size; ++i) { + if (lookup[INT_INTOBJ(ELM_LIST(exclude_obj, i)) - 1]) { + return user_param_obj; + } + } + } + // Check if the set we are trying to extend is a clique + if (include_obj != Fail && CALL_2ARGS(IsClique, digraph_obj, include_obj) == False) { + return user_param_obj; + } + // + uint16_t nr_found = 0; + uint16_t limit = (limit_obj == Infinity ? MAXVERTS : INT_INTOBJ(limit_obj)); + bool max = (max_obj == True ? true : false); // Initialise all the variable which will be used to carry out the recursion if (!init_clique_data_from_args(digraph_obj, hook_obj, @@ -443,22 +540,14 @@ Obj FuncDigraphsCliqueFinder(Obj self, Obj args) { include_obj, exclude_obj, max_obj, - size_obj, aut_grp_obj)) { return user_param_obj; } - uint16_t nr_found = 0; - uint16_t limit = (limit_obj == Infinity ? MAXVERTS : INT_INTOBJ(limit_obj)); - uint16_t size = (size_obj == Fail ? 0 : INT_INTOBJ(size_obj)); - bool max = (max_obj == True ? true : false); - if (size > GRAPH->nr_vertices) { - return user_param_obj; - } // go! if (setjmp(OUTOFHERE) == 0) { - BronKerbosch(0, 0, limit, &nr_found, max, size); + BronKerbosch(0, 0, limit, &nr_found, max, (size == 0 ? size : size - include_size)); } return user_param_obj; diff --git a/src/digraphs.c b/src/digraphs.c index ac0394632..efb42ae05 100644 --- a/src/digraphs.c +++ b/src/digraphs.c @@ -55,6 +55,7 @@ Obj IsAttributeStoringRepObj; Obj IsPermGroup; Obj IsDigraphAutomorphism; Obj LargestMovedPointPerms; +Obj IsClique; #if !defined(GAP_KERNEL_MAJOR_VERSION) || GAP_KERNEL_MAJOR_VERSION < 3 // compatibility with GAP <= 4.9 @@ -2329,6 +2330,7 @@ static Int InitKernel(StructInitInfo* module) { ImportGVarFromLibrary("IsPermGroup", &IsPermGroup); ImportGVarFromLibrary("IsDigraphAutomorphism", &IsDigraphAutomorphism); ImportGVarFromLibrary("LargestMovedPointPerms", &LargestMovedPointPerms); + ImportGVarFromLibrary("IsClique", &IsClique); /* return success */ return 0; } From 21611e5361519fb880df0c8751ff0f992069956b Mon Sep 17 00:00:00 2001 From: Julius Date: Wed, 22 Apr 2020 14:39:32 +0200 Subject: [PATCH 08/30] add: first draft --- src/cliques.c | 415 ++++++++++++++++++++++++++++++------------------- src/cliques.h | 2 +- src/digraphs.c | 8 +- src/homos.c | 4 +- 4 files changed, 264 insertions(+), 165 deletions(-) diff --git a/src/cliques.c b/src/cliques.c index 9a4ef79db..0140ba117 100644 --- a/src/cliques.c +++ b/src/cliques.c @@ -8,13 +8,11 @@ // C headers -#include // for CHAR_BIT #include // for longjmp, setjmp, jmp_buf #include // for true, false, bool #include // for NULL #include // for uint16_t, uint64_t #include // for malloc, NULL -#include // for time // GAP headers #include "src/compiled.h" @@ -22,12 +20,16 @@ // Digraphs package headers #include "bitarray.h" // for BitArray #include "conditions.h" // for Conditions -#include "digraphs-config.h" // for DIGRAPHS_HAVE___BUILTIN_CTZLL #include "digraphs-debug.h" // for DIGRAPHS_ASSERT #include "homos-graphs.h" // for Digraph, Graph, . . . #include "perms.h" // for MAXVERTS, UNDEFINED, PermColl, Perm #include "schreier-sims.h" // for PermColl, . . . -#include "cliques.h" + +//////////////////////////////////////////////////////////////////////////////// +// Macros +//////////////////////////////////////////////////////////////////////////////// + +#define MIN(a, b) (a < b ? a : b) //////////////////////////////////////////////////////////////////////////////// // Forward declarations @@ -38,13 +40,8 @@ Int DigraphNrVertices(Obj); Obj FuncOutNeighbours(Obj, Obj); Obj FuncADJACENCY_MATRIX(Obj, Obj); -// Defined in homos.c -void set_automorphisms(Obj, PermColl*); -void get_automorphism_group_from_gap(Obj, PermColl*); - // GAP level things, imported in digraphs.c extern Obj IsDigraph; -extern Obj DIGRAPHS_ValidateVertexColouring; extern Obj Infinity; extern Obj IsSymmetricDigraph; extern Obj GeneratorsOfGroup; @@ -52,40 +49,80 @@ extern Obj AutomorphismGroup; extern Obj IsPermGroup; extern Obj IsDigraphAutomorphism; extern Obj LargestMovedPointPerms; +extern Obj SmallestMovedPointPerm; extern Obj IsClique; //////////////////////////////////////////////////////////////////////////////// // Global variables //////////////////////////////////////////////////////////////////////////////// -static Obj GAP_FUNC; // Variable to hold a GAP level hook function +static Obj GAP_FUNC; // Variable to hold a GAP level hook function static Obj (*HOOK)(void*, // HOOK function applied to every homo found const BitArray*, const uint16_t); -static void* USER_PARAM; // a USER_PARAM for the hook +static void* USER_PARAM; // A USER_PARAM for the hook -// Values in MAP are restricted to those positions in IMAGE_RESTRICT -static jmp_buf OUTOFHERE; // so we can jump out of the deepest +static jmp_buf OUTOFHERE; // So we can jump out of the deepest -static Graph* GRAPH; // Graphs to hold incoming GAP symmetric digraphs +static Graph* GRAPH; // Graphs to hold incoming GAP symmetric digraphs static BitArray* CLIQUE; static Conditions* TRY; static Conditions* BAN; -static uint16_t ORB[MAXVERTS]; // Array for containing nodes in an orbit. -static BitArray* ORB_LOOKUP; // points in orbit -static PermColl* STAB_GENS[MAXVERTS]; // stabiliser generators +static uint16_t ORB[MAXVERTS]; // Array for containing nodes in an orbit. +static BitArray* ORB_LOOKUP; // points in orbit +static PermColl* STAB_GENS[MAXVERTS]; // stabiliser generators static SchreierSims* SCHREIER_SIMS; //////////////////////////////////////////////////////////////////////////////// -// +// Hook functions //////////////////////////////////////////////////////////////////////////////// -// Update a BitArray to only include one vertex per orbit of a group generated -// by STAB_GENS[rep_depth] -static void get_orbit_reps_bitarray(BitArray* bit_array, uint16_t const rep_depth) { +static Obj clique_hook_collect(void* user_param, + const BitArray* clique, + const uint16_t nr) { + UInt i; + Obj c; + + if (TNUM_OBJ((Obj) user_param) == T_PLIST_EMPTY) { + RetypeBag(user_param, T_PLIST); + } + + c = NEW_PLIST(T_PLIST, nr); + for(i = 1; i <= nr; i++) { + if (get_bit_array(clique, i - 1)){ + PushPlist(c, INTOBJ_INT(i)); + } + } + + ASS_LIST(user_param, LEN_LIST(user_param) + 1, c); + return False; +} + +static Obj clique_hook_gap(void* user_param, const BitArray* clique, const uint16_t nr) { + UInt i; + Obj c; + + c = NEW_PLIST(T_PLIST, nr); + for(i = 1; i <= nr; i++) { + if (get_bit_array(clique, i-1)) { + PushPlist(c, INTOBJ_INT(i + 1)); + } + } + + return CALL_2ARGS(GAP_FUNC, user_param, c); +} + +//////////////////////////////////////////////////////////////////////////////// +// Static helper functions +//////////////////////////////////////////////////////////////////////////////// + +// Update a BitArray to only include only one vertex per orbit with respect to +// the group generated by STAB_GENS[rep_depth] +static void get_orbit_reps_bitarray(BitArray* bit_array, + uint16_t const rep_depth) { if (STAB_GENS[rep_depth]->size == 0) { return; } @@ -95,7 +132,8 @@ static void get_orbit_reps_bitarray(BitArray* bit_array, uint16_t const rep_dept init_bit_array(ORB_LOOKUP, false, nr); while (pt < nr) { if (get_bit_array(bit_array, pt)) { - // Find the orbit of pt and remove all other points of the orbit from bit_array + // Find the orbit of pt and remove all other points of the orbit from + // set_bit_array(ORB_LOOKUP, pt, true); ORB[0] = pt; @@ -117,13 +155,194 @@ static void get_orbit_reps_bitarray(BitArray* bit_array, uint16_t const rep_dept } } +// Get the automorphism group from gap, but discard the generators which act on +// isolated vertices, in particular, if the SmallestMovedPoint is in isolated +// TODO: Are we discarding too much? +static void get_automorphism_group_from_gap(Obj digraph_obj, + PermColl* out, + BitArray* isolated) { + DIGRAPHS_ASSERT(CALL_1ARGS(IsDigraph, digraph_obj) == True); + DIGRAPHS_ASSERT(out != NULL); + Obj gens = CALL_1ARGS(GeneratorsOfGroup, CALL_1ARGS(AutomorphismGroup, digraph_obj)); + DIGRAPHS_ASSERT(IS_LIST(gens)); + clear_perm_coll(out); + out->degree = PERM_DEGREE; + DIGRAPHS_ASSERT(out->capacity >= LEN_LIST(gens)); + Int nr_added = 1; + for (Int i = 1; i <= LEN_LIST(gens); ++i) { + DIGRAPHS_ASSERT(ISB_LIST(gens, i)); + DIGRAPHS_ASSERT(IS_PERM2(ELM_LIST(gens, i)) || IS_PERM4(ELM_LIST(gens, i))); + Obj gen = ELM_LIST(gens, i); + DIGRAPHS_ASSERT(LargestMovedPointPerm(gen) <= PERM_DEGREE); + // Check if the generator acts on the isolated points + if (LargestMovedPointPerm(gen) > 0 && !get_bit_array(isolated, + INT_INTOBJ(CALL_1ARGS(SmallestMovedPointPerm, gen)) - 1)) { + Perm perm = out->perms[nr_added - 1]; + out->size++; + DIGRAPHS_ASSERT(out->size == nr_added); + size_t dep; + if (IS_PERM2(gen)) { + UInt2 const* ptp2 = CONST_ADDR_PERM2(gen); + dep = MIN((uint16_t) DEG_PERM2(gen), PERM_DEGREE); + for (uint16_t j = 0; j < dep; ++j) { + perm[j] = (uint16_t) ptp2[j]; + } + } else { + DIGRAPHS_ASSERT(IS_PERM4(gen)); + UInt4 const* ptp4 = CONST_ADDR_PERM4(gen); + dep = MIN((uint16_t) DEG_PERM4(gen), PERM_DEGREE); + for (uint16_t j = 0; j < dep; ++j) { + perm[j] = (uint16_t) ptp4[j]; + } + } + for (uint16_t j = dep; j < PERM_DEGREE; ++j) { + perm[j] = j; + } + nr_added++; + } + } +} + +static void set_automorphisms(Obj aut_grp_obj, + PermColl* out, + BitArray* isolated) { + DIGRAPHS_ASSERT(out != NULL); + clear_perm_coll(out); + out->degree = PERM_DEGREE; + Obj gens = CALL_1ARGS(GeneratorsOfGroup, aut_grp_obj); + DIGRAPHS_ASSERT(IS_LIST(gens)); + DIGRAPHS_ASSERT(LEN_LIST(gens) > 0); + for (UInt i = 1; i <= LEN_LIST(gens); ++i) { + Obj gen_obj = ELM_LIST(gens, i); + if (LargestMovedPointPerm(gen_obj) > 0 && !get_bit_array(isolated, + INT_INTOBJ(CALL_1ARGS(SmallestMovedPointPerm, gen_obj)) - 1)) { + add_perm_coll(out, new_perm_from_gap(gen_obj, PERM_DEGREE)); + } + } +} + +static void init_graph_from_digraph_obj(Graph* const graph, + Obj digraph_obj) { + DIGRAPHS_ASSERT(graph != NULL); + DIGRAPHS_ASSERT(CALL_1ARGS(IsDigraph, digraph_obj) == True); + UInt const nr = DigraphNrVertices(digraph_obj); + Obj out = FuncOutNeighbours(0L, digraph_obj); + Obj adj_mat = FuncADJACENCY_MATRIX(0L, digraph_obj); + DIGRAPHS_ASSERT(nr < MAXVERTS); + DIGRAPHS_ASSERT(IS_PLIST(adj_mat)); + DIGRAPHS_ASSERT(IS_PLIST(out)); + clear_graph(graph, nr); + + // Only include symmetric edges + for (Int i = 1; i <= nr; i++) { + Obj row = ELM_PLIST(adj_mat, i); + DIGRAPHS_ASSERT(IS_LIST(row)); + Obj zero_obj = INTOBJ_INT(0); + for (Int j = 1; j <= nr; j++) { + if (ELM_PLIST(row, j) != zero_obj && ELM_PLIST(ELM_PLIST(adj_mat,j) ,i) != zero_obj){ + add_edge_graph(graph, i - 1, j - 1); + } + } + } +} + +static bool init_data_from_args(Obj digraph_obj, + Obj hook_obj, + Obj user_param_obj, + Obj include_obj, + Obj exclude_obj, + Obj max_obj, + Obj aut_grp_obj) { + static bool is_initialized = false; + if (!is_initialized) { + is_initialized = true; + + GRAPH = new_graph(MAXVERTS); + + // Currently Conditions are a nr1 x nr1 array of BitArrays, so both + // values have to be set to MAXVERTS + CLIQUE = new_bit_array(MAXVERTS); + TRY = new_conditions(MAXVERTS, MAXVERTS); + BAN = new_conditions(MAXVERTS, MAXVERTS); + + ORB_LOOKUP = new_bit_array(MAXVERTS); + for (uint16_t i = 0; i < MAXVERTS; i++) { + STAB_GENS[i] = new_perm_coll(MAXVERTS, MAXVERTS); + } + SCHREIER_SIMS = new_schreier_sims(); + } + + uint16_t nr = DigraphNrVertices(digraph_obj); + init_graph_from_digraph_obj(GRAPH, digraph_obj); + // Get the isolated vertices of the graph + BitArray* isolated = new_bit_array(MAXVERTS); + Int first_isolated = -1; + for (uint16_t i = 0; i < nr; ++i) { + if (size_bit_array(GRAPH->neighbours[i], nr) == 0) { + if (first_isolated == -1) { + first_isolated = i; + } + set_bit_array(isolated, i, true); + } + } + + clear_conditions(TRY, nr + 1, nr); + clear_conditions(BAN, nr + 1, nr); + init_bit_array(BAN->bit_array[0], false, nr); + + init_bit_array(CLIQUE, false, nr); + // Update CLIQUE and TRY using include_obj + if (include_obj != Fail) { + set_bit_array_from_gap_list(CLIQUE, include_obj); + complement_bit_arrays(get_conditions(TRY, 0), CLIQUE, nr); + for (uint16_t i = 1; i <= LEN_LIST(include_obj); ++i) { + intersect_bit_arrays(get_conditions(TRY, 0), + GRAPH->neighbours[INT_INTOBJ(ELM_LIST(include_obj, i)) - 1], + nr); + } + } + // Update TRY using exclude_obj + if (exclude_obj != Fail) { + BitArray* exclude = new_bit_array(MAXVERTS); + set_bit_array_from_gap_list(exclude, exclude_obj); + complement_bit_arrays(get_conditions(TRY, 0), exclude, nr); + } + // Update TRY using isolated + if (first_isolated != -1) { + complement_bit_arrays(get_conditions(TRY, 0), isolated, nr); + set_bit_array(get_conditions(TRY, 0), first_isolated, true); + } + + if (hook_obj != Fail) { + GAP_FUNC = hook_obj; + HOOK = clique_hook_gap; + } else { + HOOK = clique_hook_collect; + } + USER_PARAM = user_param_obj; + + // Get generators of the automorphism group the graph + // TODO: Shouldn't we also compute the stabilizer of the group on included setwise? + PERM_DEGREE = nr; + if (aut_grp_obj == Fail) { + get_automorphism_group_from_gap(digraph_obj, STAB_GENS[0], isolated); + } else { + set_automorphisms(aut_grp_obj, STAB_GENS[0], isolated); + } + + return true; +} + +//////////////////////////////////////////////////////////////////////////////// +// Main functions +//////////////////////////////////////////////////////////////////////////////// static void BronKerbosch(uint16_t depth, uint16_t rep_depth, - uint16_t limit, - uint16_t* nr_found, + uint64_t limit, + uint64_t* nr_found, bool max, - uint16_t size) { + uint16_t size) { uint16_t nr = GRAPH->nr_vertices; BitArray* try = get_conditions(TRY, 0); @@ -222,135 +441,10 @@ static void BronKerbosch(uint16_t depth, union_bit_arrays(get_conditions(BAN,0), ORB_LOOKUP, nr); } } - - } -} - - -static Obj clique_hook_collect(void* user_param, const BitArray* clique, const uint16_t nr) { - UInt i; - Obj c; - - if (TNUM_OBJ((Obj) user_param) == T_PLIST_EMPTY) { - RetypeBag(user_param, T_PLIST); - } - - c = NEW_PLIST(T_PLIST, nr); - for(i = 1; i <= nr; i++) { - if (get_bit_array(clique, i - 1)){ - PushPlist(c, ObjInt_UInt(i)); - } - } - - ASS_LIST(user_param, LEN_LIST(user_param) + 1, c); - return False; -} - -static Obj clique_hook_gap(void* user_param, const BitArray* clique, const uint16_t nr) { - UInt i; - Obj c; - - c = NEW_PLIST(T_PLIST, nr); - for(i = 1; i <= nr; i++) { - if (get_bit_array(clique, i-1)) { - PushPlist(c, ObjInt_UInt(i + 1)); - } - } - - return CALL_2ARGS(GAP_FUNC, user_param, c); -} - -static void init_clique_graph_from_digraph_obj(Graph* const graph, - Obj digraph_obj) { - DIGRAPHS_ASSERT(graph != NULL); - DIGRAPHS_ASSERT(CALL_1ARGS(IsDigraph, digraph_obj) == True); - UInt const nr = DigraphNrVertices(digraph_obj); - Obj out = FuncOutNeighbours(0L, digraph_obj); - Obj adj_mat = FuncADJACENCY_MATRIX(0L, digraph_obj); - DIGRAPHS_ASSERT(nr < MAXVERTS); - DIGRAPHS_ASSERT(IS_PLIST(out)); - clear_graph(graph, nr); - - // Only include symmetric edges - for (uint16_t i = 1; i <= nr; i++) { - Obj row = ELM_PLIST(adj_mat, i); - DIGRAPHS_ASSERT(IS_LIST(row)); - for (uint16_t j = 1; j <= nr; j++) { - if (ELM_PLIST(row, j) != INTOBJ_INT(0) && ELM_PLIST(ELM_PLIST(adj_mat,j) ,i) != INTOBJ_INT(0)){ - add_edge_graph(graph, i - 1, j - 1); - } - } } } -static bool init_clique_data_from_args(Obj digraph_obj, - Obj hook_obj, - Obj user_param_obj, - Obj include_obj, - Obj exclude_obj, - Obj max_obj, - Obj aut_grp_obj) { - static bool is_initialized = false; - if (!is_initialized) { - is_initialized = true; - - GRAPH = new_graph(MAXVERTS); - - // Currently Conditions are a nr1 x nr1 array of BitArrays, so both - // values have to be set to MAXVERTS - CLIQUE = new_bit_array(MAXVERTS); - TRY = new_conditions(MAXVERTS, MAXVERTS); - BAN = new_conditions(MAXVERTS, MAXVERTS); - - ORB_LOOKUP = new_bit_array(MAXVERTS); - for (uint16_t i = 0; i < MAXVERTS; i++) { - STAB_GENS[i] = new_perm_coll(MAXVERTS, MAXVERTS); - } - SCHREIER_SIMS = new_schreier_sims(); - } - - init_clique_graph_from_digraph_obj(GRAPH, digraph_obj); - - uint16_t nr = DigraphNrVertices(digraph_obj); - clear_conditions(TRY, nr + 1, nr); - clear_conditions(BAN, nr + 1, nr); - init_bit_array(BAN->bit_array[0], false, nr); - - // Update and CLIQUE and TRY using include_obj - if (include_obj != Fail) { - init_bit_array(CLIQUE, false, nr); - set_bit_array_from_gap_list(CLIQUE, include_obj); - complement_bit_arrays(get_conditions(TRY, 0), CLIQUE, nr); - for (uint16_t i = 1; i <= LEN_LIST(include_obj); ++i) { - intersect_bit_arrays(get_conditions(TRY, 0), - GRAPH->neighbours[INT_INTOBJ(ELM_LIST(include_obj, i)) - 1], - nr); - } - } - - - if (hook_obj != Fail) { - GAP_FUNC = hook_obj; - HOOK = clique_hook_gap; - } else { - HOOK = clique_hook_collect; - } - USER_PARAM = user_param_obj; - - - // Get generators of the automorphism group the graph - PERM_DEGREE = nr; - if (aut_grp_obj == Fail) { - // TODO: should we use BLISS instead? Otherwise drop digraph directions in GAP - get_automorphism_group_from_gap(digraph_obj, STAB_GENS[0]); - } else { - set_automorphisms(aut_grp_obj, STAB_GENS[0]); - } - - return true; -} - -Obj FuncDigraphsCliqueFinder(Obj self, Obj args) { +Obj FuncDigraphsCliquesFinder(Obj self, Obj args) { if (LEN_PLIST(args) != 8 && LEN_PLIST(args) != 9) { ErrorQuit( "there must be 8 or 9 arguments, found %d,", LEN_PLIST(args), 0L); @@ -473,7 +567,7 @@ Obj FuncDigraphsCliqueFinder(Obj self, Obj args) { if (aut_grp_obj != Fail) { if (CALL_1ARGS(IsPermGroup, aut_grp_obj) != True) { ErrorQuit( - "the 9th argument must be a permutation group " + "the 10th argument must be a permutation group " "or fail, not %s,", (Int) TNAM_OBJ(aut_grp_obj), 0L); @@ -483,7 +577,7 @@ Obj FuncDigraphsCliqueFinder(Obj self, Obj args) { DIGRAPHS_ASSERT(LEN_LIST(gens) > 0); UInt lmp = INT_INTOBJ(CALL_1ARGS(LargestMovedPointPerms, gens)); if (lmp > 0 && LEN_LIST(gens) >= lmp) { - ErrorQuit("expected at most %d generators in the 9th argument " + ErrorQuit("expected at most %d generators in the 10th argument " "but got %d,", lmp - 1, LEN_LIST(gens)); @@ -498,7 +592,7 @@ Obj FuncDigraphsCliqueFinder(Obj self, Obj args) { } } } - + uint16_t size = (size_obj == Fail ? 0 : INT_INTOBJ(size_obj)); uint16_t include_size = (include_obj == Fail ? 0 : LEN_LIST(include_obj)); uint16_t exclude_size = (exclude_obj == Fail ? 0 : LEN_LIST(exclude_obj)); @@ -529,12 +623,11 @@ Obj FuncDigraphsCliqueFinder(Obj self, Obj args) { if (include_obj != Fail && CALL_2ARGS(IsClique, digraph_obj, include_obj) == False) { return user_param_obj; } - // - uint16_t nr_found = 0; - uint16_t limit = (limit_obj == Infinity ? MAXVERTS : INT_INTOBJ(limit_obj)); + uint64_t nr_found = 0; + uint64_t limit = (limit_obj == Infinity ? SMALLINTLIMIT : INT_INTOBJ(limit_obj)); bool max = (max_obj == True ? true : false); // Initialise all the variable which will be used to carry out the recursion - if (!init_clique_data_from_args(digraph_obj, + if (!init_data_from_args(digraph_obj, hook_obj, user_param_obj, include_obj, @@ -543,7 +636,11 @@ Obj FuncDigraphsCliqueFinder(Obj self, Obj args) { aut_grp_obj)) { return user_param_obj; } - + // The clique we are trying to extend is already big enough + if (size != 0 && include_size == size) { + HOOK(USER_PARAM, CLIQUE, nr); + return user_param_obj; + } // go! if (setjmp(OUTOFHERE) == 0) { diff --git a/src/cliques.h b/src/cliques.h index c8aa5581d..d953121b2 100644 --- a/src/cliques.h +++ b/src/cliques.h @@ -14,6 +14,6 @@ // GAP headers #include "src/compiled.h" -Obj FuncDigraphsCliqueFinder(Obj self, Obj args); +Obj FuncDigraphsCliquesFinder(Obj self, Obj args); #endif // DIGRAPHS_SRC_CLIQUES_H_ diff --git a/src/digraphs.c b/src/digraphs.c index efb42ae05..db85f7368 100644 --- a/src/digraphs.c +++ b/src/digraphs.c @@ -55,6 +55,7 @@ Obj IsAttributeStoringRepObj; Obj IsPermGroup; Obj IsDigraphAutomorphism; Obj LargestMovedPointPerms; +Obj SmallestMovedPointPerm; Obj IsClique; #if !defined(GAP_KERNEL_MAJOR_VERSION) || GAP_KERNEL_MAJOR_VERSION < 3 @@ -2252,12 +2253,12 @@ static StructGVarFunc GVarFuncs[] = { FuncHomomorphismDigraphsFinder, "src/homos.c:FuncHomomorphismDigraphsFinder"}, - {"DigraphsCliqueFinder", + {"DigraphsCliquesFinder", -1, "digraph, hook, user_param, limit, include, " "exclude, max, size", - FuncDigraphsCliqueFinder, - "src/cliques.c:FuncDigraphsCliqueFinder"}, + FuncDigraphsCliquesFinder, + "src/cliques.c:FuncDigraphsCliquesFinder"}, {"IS_PLANAR", 1, "digraph", FuncIS_PLANAR, "src/planar.c:FuncIS_PLANAR"}, @@ -2330,6 +2331,7 @@ static Int InitKernel(StructInitInfo* module) { ImportGVarFromLibrary("IsPermGroup", &IsPermGroup); ImportGVarFromLibrary("IsDigraphAutomorphism", &IsDigraphAutomorphism); ImportGVarFromLibrary("LargestMovedPointPerms", &LargestMovedPointPerms); + ImportGVarFromLibrary("SmallestMovedPointPerm", &SmallestMovedPointPerm); ImportGVarFromLibrary("IsClique", &IsClique); /* return success */ return 0; diff --git a/src/homos.c b/src/homos.c index 3ec8e8d23..52f6eba7f 100644 --- a/src/homos.c +++ b/src/homos.c @@ -294,7 +294,7 @@ homo_hook_collect(void* user_param, uint16_t const nr, uint16_t const* map) { // printf(" }>"); // } -void get_automorphism_group_from_gap(Obj digraph_obj, PermColl* out) { +static void get_automorphism_group_from_gap(Obj digraph_obj, PermColl* out) { DIGRAPHS_ASSERT(CALL_1ARGS(IsDigraph, digraph_obj) == True); Obj o = CALL_1ARGS(AutomorphismGroup, digraph_obj); o = CALL_1ARGS(GeneratorsOfGroup, o); @@ -523,7 +523,7 @@ static void internal_order_map_graph(Graph const* const graph) { } } -void set_automorphisms(Obj aut_grp_obj, PermColl* out) { +static void set_automorphisms(Obj aut_grp_obj, PermColl* out) { DIGRAPHS_ASSERT(out != NULL); clear_perm_coll(out); out->degree = PERM_DEGREE; From b704a30e920e00615713b8d9f17741a8d7d4ba67 Mon Sep 17 00:00:00 2001 From: Julius Date: Thu, 23 Apr 2020 10:56:31 +0200 Subject: [PATCH 09/30] doc: added descriptions of the arguments of the main function --- src/cliques.c | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/src/cliques.c b/src/cliques.c index 0140ba117..26c7704b7 100644 --- a/src/cliques.c +++ b/src/cliques.c @@ -444,6 +444,34 @@ static void BronKerbosch(uint16_t depth, } } +// FuncDigraphsCliquesFinder is the main function to use the C implementation +// of Bron-Kerbosch algorithm +// +// The arguments are as follows: +// +// 1. digraphs_obj the digraph to search for cliques in +// 2. hook_obj a funtion to apply to every clique found, or Fail if no +// such function is needed +// 3. user_param_obj GAP variable that can be used in the hook_obj, must be a +// plist if hook_obj is Fail +// 4. limit_obj the maximum number of cliques to find +// 5. include_obj a list of vertices of digraph_obj to required to be present in +// every clique found +// 6. exclude_obj a list of vertices of digraph_obj which cannot be present +// in any of the cliques found +// 7. max_obj True if only maximal cliques need to be found and False +// otherwise +// 8. size_obj an integer specifying the size of cliques to be found +// 9. aut_grp_obj an optional argument that can specifiy the automorphisms +// of the graph that will be used in the recursive search. +// If not given, the full automorphism group will be used. +// +// The function returns orbit representatives of cliques rather than all of the +// cliques themselves. Note that if include_obj or exlcude_obj are not invariant +// under the full automorphism group (or aut_grp_obj), then some of the cliques +// in the cliques in the orbit might not respect the inclusion/exclusion of +// the corresponding lists. + Obj FuncDigraphsCliquesFinder(Obj self, Obj args) { if (LEN_PLIST(args) != 8 && LEN_PLIST(args) != 9) { ErrorQuit( From 36f6cc46350450b78b42cd0ca7a2fc4c5c7f3a1f Mon Sep 17 00:00:00 2001 From: Julius Date: Wed, 29 Apr 2020 15:06:39 +0200 Subject: [PATCH 10/30] add: fixed small bug and now discard gens which act on the isolated vertices. --- src/cliques.c | 321 +++++++++++++++++++++---------------------------- src/digraphs.c | 14 +++ 2 files changed, 149 insertions(+), 186 deletions(-) diff --git a/src/cliques.c b/src/cliques.c index 26c7704b7..a3bb0eba6 100644 --- a/src/cliques.c +++ b/src/cliques.c @@ -23,7 +23,6 @@ #include "digraphs-debug.h" // for DIGRAPHS_ASSERT #include "homos-graphs.h" // for Digraph, Graph, . . . #include "perms.h" // for MAXVERTS, UNDEFINED, PermColl, Perm -#include "schreier-sims.h" // for PermColl, . . . //////////////////////////////////////////////////////////////////////////////// // Macros @@ -51,6 +50,13 @@ extern Obj IsDigraphAutomorphism; extern Obj LargestMovedPointPerms; extern Obj SmallestMovedPointPerm; extern Obj IsClique; +extern Obj IsTrivial; +extern Obj Orbit; +extern Obj Stabilizer; +extern Obj IsSubset; +extern Obj OnTuples; +extern Obj Group; +extern Obj ClosureGroup; //////////////////////////////////////////////////////////////////////////////// // Global variables @@ -71,10 +77,8 @@ static BitArray* CLIQUE; static Conditions* TRY; static Conditions* BAN; -static uint16_t ORB[MAXVERTS]; // Array for containing nodes in an orbit. static BitArray* ORB_LOOKUP; // points in orbit -static PermColl* STAB_GENS[MAXVERTS]; // stabiliser generators -static SchreierSims* SCHREIER_SIMS; +static Obj ORBIT; //////////////////////////////////////////////////////////////////////////////// // Hook functions @@ -119,108 +123,32 @@ static Obj clique_hook_gap(void* user_param, const BitArray* clique, const uint1 // Static helper functions //////////////////////////////////////////////////////////////////////////////// -// Update a BitArray to only include only one vertex per orbit with respect to -// the group generated by STAB_GENS[rep_depth] -static void get_orbit_reps_bitarray(BitArray* bit_array, - uint16_t const rep_depth) { - if (STAB_GENS[rep_depth]->size == 0) { +// Update a BitArray to only include one vertex per orbit with respect to +// the group +static void get_orbit_reps_bitarray(BitArray* bit_array, Obj const group) { + if (group == Fail) { return; } uint16_t nr = GRAPH->nr_vertices; - uint16_t pt = 0; - init_bit_array(ORB_LOOKUP, false, nr); - while (pt < nr) { - if (get_bit_array(bit_array, pt)) { + for (uint16_t v = 0; v < nr; ++v) { + if (get_bit_array(bit_array, v)) { // Find the orbit of pt and remove all other points of the orbit from // - set_bit_array(ORB_LOOKUP, pt, true); - ORB[0] = pt; - uint16_t n = 1; // lenght of the orbit of pt - - for (uint16_t i = 0; i < n; ++i) { - for (uint16_t j = 0; j < STAB_GENS[rep_depth]->size; ++j) { - Perm gen = STAB_GENS[rep_depth]->perms[j]; - uint16_t const img = gen[ORB[i]]; - if (!get_bit_array(ORB_LOOKUP, img)) { - ORB[n++] = img; - set_bit_array(ORB_LOOKUP, img, true); - set_bit_array(bit_array, img, false); - } - } - } - } - pt++; - } -} + ORBIT = CALL_2ARGS(Orbit, group, INTOBJ_INT(v + 1)); + DIGRAPHS_ASSERT(IS_LIST(ORBIT)); -// Get the automorphism group from gap, but discard the generators which act on -// isolated vertices, in particular, if the SmallestMovedPoint is in isolated -// TODO: Are we discarding too much? -static void get_automorphism_group_from_gap(Obj digraph_obj, - PermColl* out, - BitArray* isolated) { - DIGRAPHS_ASSERT(CALL_1ARGS(IsDigraph, digraph_obj) == True); - DIGRAPHS_ASSERT(out != NULL); - Obj gens = CALL_1ARGS(GeneratorsOfGroup, CALL_1ARGS(AutomorphismGroup, digraph_obj)); - DIGRAPHS_ASSERT(IS_LIST(gens)); - clear_perm_coll(out); - out->degree = PERM_DEGREE; - DIGRAPHS_ASSERT(out->capacity >= LEN_LIST(gens)); - Int nr_added = 1; - for (Int i = 1; i <= LEN_LIST(gens); ++i) { - DIGRAPHS_ASSERT(ISB_LIST(gens, i)); - DIGRAPHS_ASSERT(IS_PERM2(ELM_LIST(gens, i)) || IS_PERM4(ELM_LIST(gens, i))); - Obj gen = ELM_LIST(gens, i); - DIGRAPHS_ASSERT(LargestMovedPointPerm(gen) <= PERM_DEGREE); - // Check if the generator acts on the isolated points - if (LargestMovedPointPerm(gen) > 0 && !get_bit_array(isolated, - INT_INTOBJ(CALL_1ARGS(SmallestMovedPointPerm, gen)) - 1)) { - Perm perm = out->perms[nr_added - 1]; - out->size++; - DIGRAPHS_ASSERT(out->size == nr_added); - size_t dep; - if (IS_PERM2(gen)) { - UInt2 const* ptp2 = CONST_ADDR_PERM2(gen); - dep = MIN((uint16_t) DEG_PERM2(gen), PERM_DEGREE); - for (uint16_t j = 0; j < dep; ++j) { - perm[j] = (uint16_t) ptp2[j]; - } - } else { - DIGRAPHS_ASSERT(IS_PERM4(gen)); - UInt4 const* ptp4 = CONST_ADDR_PERM4(gen); - dep = MIN((uint16_t) DEG_PERM4(gen), PERM_DEGREE); - for (uint16_t j = 0; j < dep; ++j) { - perm[j] = (uint16_t) ptp4[j]; - } - } - for (uint16_t j = dep; j < PERM_DEGREE; ++j) { - perm[j] = j; + for (Int i = 1; i <= LEN_LIST(ORBIT); ++i) { + set_bit_array(bit_array, INT_INTOBJ(ELM_LIST(ORBIT, i)) - 1, false); } - nr_added++; - } - } -} - -static void set_automorphisms(Obj aut_grp_obj, - PermColl* out, - BitArray* isolated) { - DIGRAPHS_ASSERT(out != NULL); - clear_perm_coll(out); - out->degree = PERM_DEGREE; - Obj gens = CALL_1ARGS(GeneratorsOfGroup, aut_grp_obj); - DIGRAPHS_ASSERT(IS_LIST(gens)); - DIGRAPHS_ASSERT(LEN_LIST(gens) > 0); - for (UInt i = 1; i <= LEN_LIST(gens); ++i) { - Obj gen_obj = ELM_LIST(gens, i); - if (LargestMovedPointPerm(gen_obj) > 0 && !get_bit_array(isolated, - INT_INTOBJ(CALL_1ARGS(SmallestMovedPointPerm, gen_obj)) - 1)) { - add_perm_coll(out, new_perm_from_gap(gen_obj, PERM_DEGREE)); + set_bit_array(bit_array, v, true); } } } +// Initialise the graph from GAP digraph and discard non-symmetric edges and +// loops static void init_graph_from_digraph_obj(Graph* const graph, Obj digraph_obj) { DIGRAPHS_ASSERT(graph != NULL); @@ -234,25 +162,27 @@ static void init_graph_from_digraph_obj(Graph* const graph, clear_graph(graph, nr); // Only include symmetric edges - for (Int i = 1; i <= nr; i++) { + for (Int i = 1; i < nr; i++) { Obj row = ELM_PLIST(adj_mat, i); DIGRAPHS_ASSERT(IS_LIST(row)); Obj zero_obj = INTOBJ_INT(0); - for (Int j = 1; j <= nr; j++) { + for (Int j = i + 1; j <= nr; j++) { if (ELM_PLIST(row, j) != zero_obj && ELM_PLIST(ELM_PLIST(adj_mat,j) ,i) != zero_obj){ add_edge_graph(graph, i - 1, j - 1); + add_edge_graph(graph, j - 1, i - 1); } } } } -static bool init_data_from_args(Obj digraph_obj, - Obj hook_obj, - Obj user_param_obj, - Obj include_obj, - Obj exclude_obj, - Obj max_obj, - Obj aut_grp_obj) { +// Initialise the global variables +static bool init_data_from_args(Obj digraph_obj, + Obj hook_obj, + Obj user_param_obj, + Obj include_obj, + Obj exclude_obj, + Obj max_obj, + Obj* group) { static bool is_initialized = false; if (!is_initialized) { is_initialized = true; @@ -265,26 +195,12 @@ static bool init_data_from_args(Obj digraph_obj, TRY = new_conditions(MAXVERTS, MAXVERTS); BAN = new_conditions(MAXVERTS, MAXVERTS); - ORB_LOOKUP = new_bit_array(MAXVERTS); - for (uint16_t i = 0; i < MAXVERTS; i++) { - STAB_GENS[i] = new_perm_coll(MAXVERTS, MAXVERTS); - } - SCHREIER_SIMS = new_schreier_sims(); + ORBIT = Fail; + ORB_LOOKUP = new_bit_array(MAXVERTS); } uint16_t nr = DigraphNrVertices(digraph_obj); init_graph_from_digraph_obj(GRAPH, digraph_obj); - // Get the isolated vertices of the graph - BitArray* isolated = new_bit_array(MAXVERTS); - Int first_isolated = -1; - for (uint16_t i = 0; i < nr; ++i) { - if (size_bit_array(GRAPH->neighbours[i], nr) == 0) { - if (first_isolated == -1) { - first_isolated = i; - } - set_bit_array(isolated, i, true); - } - } clear_conditions(TRY, nr + 1, nr); clear_conditions(BAN, nr + 1, nr); @@ -307,11 +223,41 @@ static bool init_data_from_args(Obj digraph_obj, set_bit_array_from_gap_list(exclude, exclude_obj); complement_bit_arrays(get_conditions(TRY, 0), exclude, nr); } - // Update TRY using isolated + + // Get the isolated vertices of the graph + BitArray* isolated = new_bit_array(MAXVERTS); + Int first_isolated = -1; + for (uint16_t i = 0; i < nr; ++i) { + if (size_bit_array(GRAPH->neighbours[i], nr) == 0) { + if (first_isolated == -1 && get_bit_array(get_conditions(TRY, 0), i)) { + first_isolated = i; + } + set_bit_array(isolated, i, true); + } + } + // Update TRY using isolated, only one isolated vertex is used if (first_isolated != -1) { complement_bit_arrays(get_conditions(TRY, 0), isolated, nr); set_bit_array(get_conditions(TRY, 0), first_isolated, true); } + + // Discard the generators of aut_grp_obj which act on the isolated vertices + if (size_bit_array(isolated, nr) > 0) { + Obj new_group = Fail; + Obj gens = CALL_1ARGS(GeneratorsOfGroup, *group); + DIGRAPHS_ASSERT(IS_LIST(gens)); + for (Int i = 1; i <= LEN_LIST(gens); ++i) { + Obj s = CALL_1ARGS(SmallestMovedPointPerm, ELM_LIST(gens, i)); + if (s != Infinity && !get_bit_array(isolated, INT_INTOBJ(s) - 1)) { + if (new_group == Fail) { + new_group = CALL_1ARGS(Group, ELM_LIST(gens, i)); + } else { + new_group = CALL_2ARGS(ClosureGroup, new_group, ELM_LIST(gens, i)); + } + } + } + *group = new_group; + } if (hook_obj != Fail) { GAP_FUNC = hook_obj; @@ -321,15 +267,6 @@ static bool init_data_from_args(Obj digraph_obj, } USER_PARAM = user_param_obj; - // Get generators of the automorphism group the graph - // TODO: Shouldn't we also compute the stabilizer of the group on included setwise? - PERM_DEGREE = nr; - if (aut_grp_obj == Fail) { - get_automorphism_group_from_gap(digraph_obj, STAB_GENS[0], isolated); - } else { - set_automorphisms(aut_grp_obj, STAB_GENS[0], isolated); - } - return true; } @@ -342,7 +279,8 @@ static void BronKerbosch(uint16_t depth, uint64_t limit, uint64_t* nr_found, bool max, - uint16_t size) { + uint16_t size, + Obj group) { uint16_t nr = GRAPH->nr_vertices; BitArray* try = get_conditions(TRY, 0); @@ -355,15 +293,14 @@ static void BronKerbosch(uint16_t depth, if (*nr_found >= limit) { longjmp(OUTOFHERE, 1); } - } else if (size_bit_array(try, nr) == 0 && size_bit_array(ban, nr) == 0) { + } else if (size_bit_array(try, nr) == 0 && size_bit_array(ban, nr) == 0 && + (size == 0 || size == depth)) { // is a maximal clique - if (size == 0 || size == depth) { - HOOK(USER_PARAM, CLIQUE, nr); - *nr_found += 1; - if (*nr_found >= limit) { - longjmp(OUTOFHERE, 1); - } - } + HOOK(USER_PARAM, CLIQUE, nr); + *nr_found += 1; + if (*nr_found >= limit) { + longjmp(OUTOFHERE, 1); + } return; } @@ -371,10 +308,11 @@ static void BronKerbosch(uint16_t depth, if (max) { // Choose a pivot with as many neighbours in as possible uint16_t pivot = 0; - int max_neighbours = -1; + int16_t max_neighbours = -1; + + BitArray* copy_try = new_bit_array(MAXVERTS); for (uint16_t i = 0; i < nr; i++){ if (get_bit_array(try, i) || get_bit_array(ban, i)){ - BitArray* copy_try = new_bit_array(MAXVERTS); // reuse it! copy_bit_array(copy_try, try, nr); intersect_bit_arrays(copy_try, GRAPH->neighbours[i], nr); uint16_t num_neighbours = size_bit_array(copy_try, nr); @@ -395,48 +333,36 @@ static void BronKerbosch(uint16_t depth, } // Get orbit representatives of - get_orbit_reps_bitarray(to_try, rep_depth); + get_orbit_reps_bitarray(to_try, group); - for (uint16_t pt = 0; pt < nr; pt++) { - if (get_bit_array(to_try, pt)){ - set_bit_array(CLIQUE, pt, true); + for (uint16_t v = 0; v < nr; v++) { + if (get_bit_array(to_try, v)){ + set_bit_array(CLIQUE, v, true); - push_conditions(TRY, depth + 1, 0, GRAPH->neighbours[pt]); - push_conditions(BAN, depth + 1, 0, GRAPH->neighbours[pt]); + push_conditions(TRY, depth + 1, 0, GRAPH->neighbours[v]); + push_conditions(BAN, depth + 1, 0, GRAPH->neighbours[v]); // recurse - if (STAB_GENS[rep_depth]->size == 0) { - BronKerbosch(depth + 1, rep_depth, limit, nr_found, max, size); + if (group == Fail) { + BronKerbosch(depth + 1, rep_depth, limit, nr_found, max, size, group); } else { - // the point_stabilizer is very SLOW! - point_stabilizer(SCHREIER_SIMS, STAB_GENS[rep_depth], - STAB_GENS[rep_depth + 1], pt); - BronKerbosch(depth + 1, rep_depth + 1, limit, nr_found, max, size); + Obj stabiliser = CALL_2ARGS(Stabilizer, group, INTOBJ_INT(v + 1)); + if (CALL_1ARGS(IsTrivial, stabiliser) == True) { + stabiliser = Fail; + } + BronKerbosch(depth + 1, rep_depth + 1, limit, nr_found, max, size, stabiliser); } pop_conditions(TRY, depth + 1); pop_conditions(BAN, depth + 1); - set_bit_array(CLIQUE, pt, false); + set_bit_array(CLIQUE, v, false); - if (STAB_GENS[rep_depth]->size == 0) { - set_bit_array(get_conditions(TRY, 0), pt, false); - set_bit_array(get_conditions(BAN, 0), pt, true); + if (group == Fail) { + set_bit_array(get_conditions(TRY, 0), v, false); + set_bit_array(get_conditions(BAN, 0), v, true); } else { - init_bit_array(ORB_LOOKUP, false, nr); - set_bit_array(ORB_LOOKUP, pt, true); - ORB[0] = pt; - uint16_t n = 1; // lenght of the orbit of pt - - for (uint16_t i = 0; i < n; ++i) { - for (uint16_t j = 0; j < STAB_GENS[rep_depth]->size; ++j) { - Perm gen = STAB_GENS[rep_depth]->perms[j]; - uint16_t const img = gen[ORB[i]]; - if (!get_bit_array(ORB_LOOKUP, img)) { - ORB[n++] = img; - set_bit_array(ORB_LOOKUP, img, true); - } - } - } + ORBIT = CALL_2ARGS(Orbit, group, INTOBJ_INT(v + 1)); + set_bit_array_from_gap_list(ORB_LOOKUP, ORBIT); complement_bit_arrays(get_conditions(TRY,0), ORB_LOOKUP, nr); union_bit_arrays(get_conditions(BAN,0), ORB_LOOKUP, nr); } @@ -456,9 +382,13 @@ static void BronKerbosch(uint16_t depth, // plist if hook_obj is Fail // 4. limit_obj the maximum number of cliques to find // 5. include_obj a list of vertices of digraph_obj to required to be present in -// every clique found +// every clique found. The list needs to be invariant under +// aut_grp_obj or the full automorphism group if aut_grp_obj +// is Fail // 6. exclude_obj a list of vertices of digraph_obj which cannot be present -// in any of the cliques found +// in any of the cliques found. The list needs to be +// invariant under aut_grp_obj or the full automorphism +// group if aut_grp_obj is Fail // 7. max_obj True if only maximal cliques need to be found and False // otherwise // 8. size_obj an integer specifying the size of cliques to be found @@ -466,11 +396,11 @@ static void BronKerbosch(uint16_t depth, // of the graph that will be used in the recursive search. // If not given, the full automorphism group will be used. // -// The function returns orbit representatives of cliques rather than all of the -// cliques themselves. Note that if include_obj or exlcude_obj are not invariant -// under the full automorphism group (or aut_grp_obj), then some of the cliques -// in the cliques in the orbit might not respect the inclusion/exclusion of -// the corresponding lists. +// Remarks: +// 1. The function returns orbit representatives of cliques rather than all of the +// cliques themselves. +// 2. Only one isolated vertex will be returned even if aut_grp_obj does not +// act transitevely on all isolated vertices. Obj FuncDigraphsCliquesFinder(Obj self, Obj args) { if (LEN_PLIST(args) != 8 && LEN_PLIST(args) != 9) { @@ -595,7 +525,7 @@ Obj FuncDigraphsCliquesFinder(Obj self, Obj args) { if (aut_grp_obj != Fail) { if (CALL_1ARGS(IsPermGroup, aut_grp_obj) != True) { ErrorQuit( - "the 10th argument must be a permutation group " + "the 9th argument must be a permutation group " "or fail, not %s,", (Int) TNAM_OBJ(aut_grp_obj), 0L); @@ -605,7 +535,7 @@ Obj FuncDigraphsCliquesFinder(Obj self, Obj args) { DIGRAPHS_ASSERT(LEN_LIST(gens) > 0); UInt lmp = INT_INTOBJ(CALL_1ARGS(LargestMovedPointPerms, gens)); if (lmp > 0 && LEN_LIST(gens) >= lmp) { - ErrorQuit("expected at most %d generators in the 10th argument " + ErrorQuit("expected at most %d generators in the 9th argument " "but got %d,", lmp - 1, LEN_LIST(gens)); @@ -619,6 +549,25 @@ Obj FuncDigraphsCliquesFinder(Obj self, Obj args) { 0L); } } + } else { + aut_grp_obj = CALL_1ARGS(AutomorphismGroup, digraph_obj); + } + + // Check that include_obj and exclude_obj are invariant under aut_grp_obj + Obj gens = CALL_1ARGS(GeneratorsOfGroup, aut_grp_obj); + DIGRAPHS_ASSERT(IS_LIST(gens)); + DIGRAPHS_ASSERT(LEN_LIST(gens) > 0); + for (UInt i = 1; i <= LEN_LIST(gens); ++i) { + if (include_obj != Fail && CALL_2ARGS(IsSubset, include_obj, + CALL_2ARGS(OnTuples, include_obj, ELM_LIST(gens, i))) != True) { + ErrorQuit("the 5th argument must be invaraint under , " + "or the full automorphism if is not given", 0L, 0L); + } + if (exclude_obj != Fail && CALL_2ARGS(IsSubset, exclude_obj, + CALL_2ARGS(OnTuples, exclude_obj, ELM_LIST(gens, i))) != True) { + ErrorQuit("the 6th argument must be invaraint under , " + "or the full automorphism if is not given", 0L, 0L); + } } uint16_t size = (size_obj == Fail ? 0 : INT_INTOBJ(size_obj)); @@ -656,12 +605,12 @@ Obj FuncDigraphsCliquesFinder(Obj self, Obj args) { bool max = (max_obj == True ? true : false); // Initialise all the variable which will be used to carry out the recursion if (!init_data_from_args(digraph_obj, - hook_obj, - user_param_obj, - include_obj, - exclude_obj, - max_obj, - aut_grp_obj)) { + hook_obj, + user_param_obj, + include_obj, + exclude_obj, + max_obj, + &aut_grp_obj)) { return user_param_obj; } // The clique we are trying to extend is already big enough @@ -672,7 +621,7 @@ Obj FuncDigraphsCliquesFinder(Obj self, Obj args) { // go! if (setjmp(OUTOFHERE) == 0) { - BronKerbosch(0, 0, limit, &nr_found, max, (size == 0 ? size : size - include_size)); + BronKerbosch(0, 0, limit, &nr_found, max, (size == 0 ? size : size - include_size), aut_grp_obj); } return user_param_obj; diff --git a/src/digraphs.c b/src/digraphs.c index db85f7368..66bf05af9 100644 --- a/src/digraphs.c +++ b/src/digraphs.c @@ -57,6 +57,13 @@ Obj IsDigraphAutomorphism; Obj LargestMovedPointPerms; Obj SmallestMovedPointPerm; Obj IsClique; +Obj IsTrivial; +Obj Orbit; +Obj Stabilizer; +Obj IsSubset; +Obj OnTuples; +Obj Group; +Obj ClosureGroup; #if !defined(GAP_KERNEL_MAJOR_VERSION) || GAP_KERNEL_MAJOR_VERSION < 3 // compatibility with GAP <= 4.9 @@ -2333,6 +2340,13 @@ static Int InitKernel(StructInitInfo* module) { ImportGVarFromLibrary("LargestMovedPointPerms", &LargestMovedPointPerms); ImportGVarFromLibrary("SmallestMovedPointPerm", &SmallestMovedPointPerm); ImportGVarFromLibrary("IsClique", &IsClique); + ImportGVarFromLibrary("IsTrivial", &IsTrivial); + ImportGVarFromLibrary("Orbit", &Orbit); + ImportGVarFromLibrary("Stabilizer", &Stabilizer); + ImportGVarFromLibrary("IsSubset", &IsSubset); + ImportGVarFromLibrary("OnTuples", &OnTuples); + ImportGVarFromLibrary("Group", &Group); + ImportGVarFromLibrary("ClosureGroup", &ClosureGroup); /* return success */ return 0; } From 7d94589e6d4dba721fc7616eef67f4695da0f8b9 Mon Sep 17 00:00:00 2001 From: Julius Date: Mon, 4 May 2020 17:33:14 +0200 Subject: [PATCH 11/30] add: CliqueFinder uses C code when possible --- gap/cliques.gi | 164 +++++++++++++++++++++++++++++++++++++++++-------- src/cliques.c | 12 ++-- 2 files changed, 144 insertions(+), 32 deletions(-) diff --git a/gap/cliques.gi b/gap/cliques.gi index 42ae1b065..9399bbdb5 100644 --- a/gap/cliques.gi +++ b/gap/cliques.gi @@ -541,12 +541,16 @@ function(arg) return CliquesFinder(D, fail, [], limit, include, exclude, true, size, false); end); + +# A wrapper for DigraphsCliquesFinder +# This is very hacky at the moment, so we could test C code with GAP tests InstallGlobalFunction(CliquesFinder, -function(D, hook, user_param, limit, include, exclude, max, size, reps) - local n, sub, group, invariant_include, invariant_exclude, include_variant, - exclude_variant, x, v, o, i, out; +function(digraph, hook, user_param, limit, include, exclude, max, size, reps) + local n, subgraph, group, vertices, invariant_include, invariant_exclude, + include_variant, exclude_variant, include_invariant, exclude_invariant, + x, v, o, i, out, out_reps, found_orbits, num_found, add_cliques; - if not IsDigraph(D) then + if not IsDigraph(digraph) then ErrorNoReturn("the 1st argument must be a digraph,"); fi; @@ -565,7 +569,7 @@ function(D, hook, user_param, limit, include, exclude, max, size, reps) "a positive integer,"); fi; - n := DigraphNrVertices(D); + n := DigraphNrVertices(digraph); if not (IsHomogeneousList(include) and ForAll(include, x -> IsPosInt(x) and x <= n) and IsDuplicateFreeList(include)) @@ -591,21 +595,25 @@ function(D, hook, user_param, limit, include, exclude, max, size, reps) ErrorNoReturn("the 9th argument must be true or false,"); fi; - # Investigate whether and are invariant under - sub := DigraphMutableCopyIfMutable(D); - sub := MaximalSymmetricSubdigraphWithoutLoops(sub); - group := AutomorphismGroup(sub); + subgraph := DigraphMutableCopyIfMutable(digraph); + subgraph := MaximalSymmetricSubdigraphWithoutLoops(subgraph); + group := AutomorphismGroup(subgraph); + # Investigate whether and are invariant under + vertices := DigraphVertices(digraph); + include_variant := BlistList(vertices, []); + exclude_variant := BlistList(vertices, []); invariant_include := true; + include_invariant := include; invariant_exclude := true; - include_variant := []; - exclude_variant := []; + exclude_invariant := exclude; if not IsTrivial(group) and (not IsEmpty(include) or not IsEmpty(exclude)) then if not ForAll(GeneratorsOfGroup(group), x -> IsSubset(include, OnTuples(include, x))) then invariant_include := false; + include_invariant := []; if not reps then x := ShallowCopy(include); while not IsEmpty(x) do @@ -613,7 +621,9 @@ function(D, hook, user_param, limit, include, exclude, max, size, reps) o := List(Orbit(group, v)); i := Intersection(x, o); if not IsSubset(x, o) then - Append(include_variant, i); + UniteBlist(include_variant, BlistList(vertices, i)); + else + Append(include_invariant, i); fi; x := Difference(x, i); od; @@ -622,6 +632,7 @@ function(D, hook, user_param, limit, include, exclude, max, size, reps) if not ForAll(GeneratorsOfGroup(group), x -> IsSubset(exclude, OnTuples(exclude, x))) then invariant_exclude := false; + exclude_invariant := []; if not reps then x := ShallowCopy(exclude); while not IsEmpty(x) do @@ -629,7 +640,9 @@ function(D, hook, user_param, limit, include, exclude, max, size, reps) o := List(Orbit(group, v)); i := Intersection(x, o); if not IsSubset(x, o) then - Append(exclude_variant, i); + UniteBlist(exclude_variant, BlistList(vertices, i)); + else + Append(exclude_invariant, i); fi; x := Difference(x, i); od; @@ -641,21 +654,120 @@ function(D, hook, user_param, limit, include, exclude, max, size, reps) "5th arguments and must be ", "invariant under the action of the automorphism group of ", "the maximal symmetric subdigraph without loops,"); - fi; + fi; fi; - out := DIGRAPHS_BronKerbosch(D, - hook, - user_param, - limit, - include, - exclude, - max, - size, - reps, - include_variant, - exclude_variant); - return MakeImmutable(out); + if 0 < DigraphNrVertices(digraph) and DigraphNrVertices(digraph) < 512 then + if reps then + # Might want to pass the group here + out := DigraphsCliquesFinder(subgraph, + hook, + user_param, + limit, + include, + exclude, + max, + size); + return MakeImmutable(out); + else + out_reps := DigraphsCliquesFinder(subgraph, + fail, + [], + limit, + include_invariant, + exclude_invariant, + max, + size); + + # Function to find the valid cliques of an orbit given an orbit rep + found_orbits := []; + num_found := 0; + if hook = fail then + hook := Add; + fi; + + add_cliques := function(clique) + local orbit, n, i; + + if not ForAny(found_orbits, x -> clique in x) then + orbit := Orb(group, clique, OnSets); + Enumerate(orbit); + Add(found_orbits, orbit); + n := Length(orbit); + + if invariant_include and invariant_exclude then + # we're not just looking for orbit reps, but inc and exc are invariant + # so there is nothing extra to check + n := Minimum(limit - num_found, n); + for clique in orbit{[1 .. n]} do + hook(user_param, clique); + od; + num_found := num_found + n; + return; + fi; + + if invariant_include then + # Cliques in the orbit might contain forbidden vertices + i := 0; + while i < n and num_found < limit do + i := i + 1; + clique := BlistList(vertices, orbit[i]); + if SizeBlist(IntersectionBlist(exclude_variant, clique)) = 0 then + hook(user_param, orbit[i]); + num_found := num_found + 1; + fi; + od; + elif invariant_exclude then + # Cliques in the orbit might not contain all required vertices + i := 0; + while i < n and num_found < limit do + i := i + 1; + clique := BlistList(vertices, orbit[i]); + if IsSubsetBlist(clique, include_variant) then + hook(user_param, orbit[i]); + num_found := num_found + 1; + fi; + od; + else + # Cliques in the orbit might contain forbidden vertices and + # might not contain all required vertices + i := 0; + while i < n and num_found < limit do + i := i + 1; + clique := BlistList(vertices, orbit[i]); + if SizeBlist(IntersectionBlist(exclude_variant, clique)) = 0 + and IsSubsetBlist(clique, include_variant) then + hook(user_param, orbit[i]); + num_found := num_found + 1; + fi; + od; + fi; + fi; + return; + end; + + for x in out_reps do + add_cliques(x); + od; + return MakeImmutable(user_param); + fi; + else + include_variant := ListBlist(vertices, include_variant); + exclude_variant := ListBlist(vertices, exclude_variant); + + out := DIGRAPHS_BronKerbosch(digraph, + hook, + user_param, + limit, + include, + exclude, + max, + size, + reps, + include_variant, + exclude_variant); + return MakeImmutable(out); + fi; end); InstallGlobalFunction(DIGRAPHS_BronKerbosch, diff --git a/src/cliques.c b/src/cliques.c index a3bb0eba6..71b3ff377 100644 --- a/src/cliques.c +++ b/src/cliques.c @@ -1,12 +1,11 @@ //////////////////////////////////////////////////////////////////////////////// // -// cliques.c di/cliques Julius Jonusas +// cliques.c cliques Julius Jonusas // // Copyright (C) 2020 - Julius Jonusas // // This file is free software, see the digraphs/LICENSE. - // C headers #include // for longjmp, setjmp, jmp_buf #include // for true, false, bool @@ -303,7 +302,8 @@ static void BronKerbosch(uint16_t depth, } return; } - + + // TODO: BitArray* to_try = new_bit_array(MAXVERTS); if (max) { // Choose a pivot with as many neighbours in as possible @@ -426,10 +426,10 @@ Obj FuncDigraphsCliquesFinder(Obj self, Obj args) { (Int) TNAM_OBJ(digraph_obj), 0L); } else if (DigraphNrVertices(digraph_obj) > MAXVERTS) { - ErrorQuit("the 1st argument must have at most 512 vertices, " + ErrorQuit("the 1st argument must have at most %d vertices, " "found %d,", - DigraphNrVertices(digraph_obj), - 0L); + MAXVERTS, + DigraphNrVertices(digraph_obj)); } if (hook_obj == Fail) { if (!IS_LIST(user_param_obj) || !IS_MUTABLE_OBJ(user_param_obj)) { From 53ec30759a6303df38f14e3e7ccb92b73f9e92cf Mon Sep 17 00:00:00 2001 From: Julius Date: Wed, 13 May 2020 10:54:09 +0200 Subject: [PATCH 12/30] add: implemented suggested technical changes --- gap/cliques.gi | 2 +- src/cliques.c | 238 +++++++++++++++++++++++++------------------------ 2 files changed, 123 insertions(+), 117 deletions(-) diff --git a/gap/cliques.gi b/gap/cliques.gi index 9399bbdb5..b76605a11 100644 --- a/gap/cliques.gi +++ b/gap/cliques.gi @@ -657,7 +657,7 @@ function(digraph, hook, user_param, limit, include, exclude, max, size, reps) fi; fi; - if 0 < DigraphNrVertices(digraph) and DigraphNrVertices(digraph) < 512 then + if DigraphNrVertices(digraph) < 512 then if reps then # Might want to pass the group here out := DigraphsCliquesFinder(subgraph, diff --git a/src/cliques.c b/src/cliques.c index 71b3ff377..234b763ca 100644 --- a/src/cliques.c +++ b/src/cliques.c @@ -7,7 +7,6 @@ // This file is free software, see the digraphs/LICENSE. // C headers -#include // for longjmp, setjmp, jmp_buf #include // for true, false, bool #include // for NULL #include // for uint16_t, uint64_t @@ -28,6 +27,7 @@ //////////////////////////////////////////////////////////////////////////////// #define MIN(a, b) (a < b ? a : b) +#define EXIT 0 //////////////////////////////////////////////////////////////////////////////// // Forward declarations @@ -61,23 +61,25 @@ extern Obj ClosureGroup; // Global variables //////////////////////////////////////////////////////////////////////////////// -static Obj GAP_FUNC; // Variable to hold a GAP level hook function - -static Obj (*HOOK)(void*, // HOOK function applied to every homo found - const BitArray*, - const uint16_t); -static void* USER_PARAM; // A USER_PARAM for the hook - -static jmp_buf OUTOFHERE; // So we can jump out of the deepest - -static Graph* GRAPH; // Graphs to hold incoming GAP symmetric digraphs - -static BitArray* CLIQUE; -static Conditions* TRY; -static Conditions* BAN; +struct clique_data { + void* user_param; // A USER_PARAM for the hook + Obj gap_func; // Variable to hold a GAP level hook function + Obj (*hook)(void*, // HOOK function applied to every homo found + const BitArray*, + const uint16_t, + Obj); + + Graph* graph; // Graphs to hold incoming GAP symmetric digraphs + BitArray* clique; + Conditions* try; + Conditions* ban; + Conditions* to_try; + + BitArray* temp_bitarray; + Obj orbit; +}; -static BitArray* ORB_LOOKUP; // points in orbit -static Obj ORBIT; +typedef struct clique_data CliqueData; //////////////////////////////////////////////////////////////////////////////// // Hook functions @@ -85,14 +87,11 @@ static Obj ORBIT; static Obj clique_hook_collect(void* user_param, const BitArray* clique, - const uint16_t nr) { + const uint16_t nr, + Obj gap_func) { UInt i; Obj c; - if (TNUM_OBJ((Obj) user_param) == T_PLIST_EMPTY) { - RetypeBag(user_param, T_PLIST); - } - c = NEW_PLIST(T_PLIST, nr); for(i = 1; i <= nr; i++) { if (get_bit_array(clique, i - 1)){ @@ -104,7 +103,7 @@ static Obj clique_hook_collect(void* user_param, return False; } -static Obj clique_hook_gap(void* user_param, const BitArray* clique, const uint16_t nr) { +static Obj clique_hook_gap(void* user_param, const BitArray* clique, const uint16_t nr, Obj gap_func) { UInt i; Obj c; @@ -115,7 +114,7 @@ static Obj clique_hook_gap(void* user_param, const BitArray* clique, const uint1 } } - return CALL_2ARGS(GAP_FUNC, user_param, c); + return CALL_2ARGS(gap_func, user_param, c); } //////////////////////////////////////////////////////////////////////////////// @@ -124,22 +123,24 @@ static Obj clique_hook_gap(void* user_param, const BitArray* clique, const uint1 // Update a BitArray to only include one vertex per orbit with respect to // the group -static void get_orbit_reps_bitarray(BitArray* bit_array, Obj const group) { +static void get_orbit_reps_bitarray(BitArray* bit_array, + Obj const group, + CliqueData* data) { if (group == Fail) { return; } - uint16_t nr = GRAPH->nr_vertices; + uint16_t nr = data->graph->nr_vertices; for (uint16_t v = 0; v < nr; ++v) { if (get_bit_array(bit_array, v)) { // Find the orbit of pt and remove all other points of the orbit from // - ORBIT = CALL_2ARGS(Orbit, group, INTOBJ_INT(v + 1)); - DIGRAPHS_ASSERT(IS_LIST(ORBIT)); + data->orbit = CALL_2ARGS(Orbit, group, INTOBJ_INT(v + 1)); + DIGRAPHS_ASSERT(IS_LIST(data->orbit)); - for (Int i = 1; i <= LEN_LIST(ORBIT); ++i) { - set_bit_array(bit_array, INT_INTOBJ(ELM_LIST(ORBIT, i)) - 1, false); + for (Int i = 1; i <= LEN_LIST(data->orbit); ++i) { + set_bit_array(bit_array, INT_INTOBJ(ELM_LIST(data->orbit, i)) - 1, false); } set_bit_array(bit_array, v, true); } @@ -181,38 +182,41 @@ static bool init_data_from_args(Obj digraph_obj, Obj include_obj, Obj exclude_obj, Obj max_obj, - Obj* group) { - static bool is_initialized = false; - if (!is_initialized) { - is_initialized = true; + Obj* group, + CliqueData* data) { + static bool is_initialised = false; + if (!is_initialised) { + is_initialised = true; - GRAPH = new_graph(MAXVERTS); + data->graph = new_graph(MAXVERTS); // Currently Conditions are a nr1 x nr1 array of BitArrays, so both // values have to be set to MAXVERTS - CLIQUE = new_bit_array(MAXVERTS); - TRY = new_conditions(MAXVERTS, MAXVERTS); - BAN = new_conditions(MAXVERTS, MAXVERTS); + data->clique = new_bit_array(MAXVERTS); + data->try = new_conditions(MAXVERTS, MAXVERTS); + data->ban = new_conditions(MAXVERTS, MAXVERTS); + data->to_try = new_conditions(MAXVERTS, MAXVERTS); - ORBIT = Fail; - ORB_LOOKUP = new_bit_array(MAXVERTS); + data->orbit = Fail; + data->temp_bitarray = new_bit_array(MAXVERTS); } uint16_t nr = DigraphNrVertices(digraph_obj); - init_graph_from_digraph_obj(GRAPH, digraph_obj); + init_graph_from_digraph_obj(data->graph, digraph_obj); - clear_conditions(TRY, nr + 1, nr); - clear_conditions(BAN, nr + 1, nr); - init_bit_array(BAN->bit_array[0], false, nr); + clear_conditions(data->try, nr + 1, nr); + clear_conditions(data->ban, nr + 1, nr); + clear_conditions(data->to_try, nr + 1, nr); + init_bit_array(data->ban->bit_array[0], false, nr); - init_bit_array(CLIQUE, false, nr); + init_bit_array(data->clique, false, nr); // Update CLIQUE and TRY using include_obj if (include_obj != Fail) { - set_bit_array_from_gap_list(CLIQUE, include_obj); - complement_bit_arrays(get_conditions(TRY, 0), CLIQUE, nr); + set_bit_array_from_gap_list(data->clique, include_obj); + complement_bit_arrays(get_conditions(data->try, 0), data->clique, nr); for (uint16_t i = 1; i <= LEN_LIST(include_obj); ++i) { - intersect_bit_arrays(get_conditions(TRY, 0), - GRAPH->neighbours[INT_INTOBJ(ELM_LIST(include_obj, i)) - 1], + intersect_bit_arrays(get_conditions(data->try, 0), + data->graph->neighbours[INT_INTOBJ(ELM_LIST(include_obj, i)) - 1], nr); } } @@ -220,15 +224,15 @@ static bool init_data_from_args(Obj digraph_obj, if (exclude_obj != Fail) { BitArray* exclude = new_bit_array(MAXVERTS); set_bit_array_from_gap_list(exclude, exclude_obj); - complement_bit_arrays(get_conditions(TRY, 0), exclude, nr); + complement_bit_arrays(get_conditions(data->try, 0), exclude, nr); } // Get the isolated vertices of the graph BitArray* isolated = new_bit_array(MAXVERTS); Int first_isolated = -1; for (uint16_t i = 0; i < nr; ++i) { - if (size_bit_array(GRAPH->neighbours[i], nr) == 0) { - if (first_isolated == -1 && get_bit_array(get_conditions(TRY, 0), i)) { + if (size_bit_array(data->graph->neighbours[i], nr) == 0) { + if (first_isolated == -1 && get_bit_array(get_conditions(data->try, 0), i)) { first_isolated = i; } set_bit_array(isolated, i, true); @@ -236,8 +240,8 @@ static bool init_data_from_args(Obj digraph_obj, } // Update TRY using isolated, only one isolated vertex is used if (first_isolated != -1) { - complement_bit_arrays(get_conditions(TRY, 0), isolated, nr); - set_bit_array(get_conditions(TRY, 0), first_isolated, true); + complement_bit_arrays(get_conditions(data->try, 0), isolated, nr); + set_bit_array(get_conditions(data->try, 0), first_isolated, true); } // Discard the generators of aut_grp_obj which act on the isolated vertices @@ -259,12 +263,13 @@ static bool init_data_from_args(Obj digraph_obj, } if (hook_obj != Fail) { - GAP_FUNC = hook_obj; - HOOK = clique_hook_gap; + data->gap_func = hook_obj; + data->hook = clique_hook_gap; } else { - HOOK = clique_hook_collect; + data->gap_func = Fail; + data->hook = clique_hook_collect; } - USER_PARAM = user_param_obj; + data->user_param = user_param_obj; return true; } @@ -273,49 +278,47 @@ static bool init_data_from_args(Obj digraph_obj, // Main functions //////////////////////////////////////////////////////////////////////////////// -static void BronKerbosch(uint16_t depth, - uint16_t rep_depth, - uint64_t limit, - uint64_t* nr_found, - bool max, - uint16_t size, - Obj group) { +static int BronKerbosch(uint16_t depth, + uint16_t rep_depth, + uint64_t limit, + uint64_t* nr_found, + bool max, + uint16_t size, + Obj group, + CliqueData* data) { - uint16_t nr = GRAPH->nr_vertices; - BitArray* try = get_conditions(TRY, 0); - BitArray* ban = get_conditions(BAN, 0); + uint16_t nr = data->graph->nr_vertices; + BitArray* try = get_conditions(data->try, 0); + BitArray* ban = get_conditions(data->ban, 0); if (depth > 0 && !max && ( size == 0 || size == depth)) { // We are not looking for maximal cliques - HOOK(USER_PARAM, CLIQUE, nr); + data->hook(data->user_param, data->clique, nr, data->gap_func); *nr_found += 1; if (*nr_found >= limit) { - longjmp(OUTOFHERE, 1); + return EXIT; } } else if (size_bit_array(try, nr) == 0 && size_bit_array(ban, nr) == 0 && (size == 0 || size == depth)) { // is a maximal clique - HOOK(USER_PARAM, CLIQUE, nr); + data->hook(data->user_param, data->clique, nr, data->gap_func); *nr_found += 1; if (*nr_found >= limit) { - longjmp(OUTOFHERE, 1); + return EXIT; } - return; } - // TODO: - BitArray* to_try = new_bit_array(MAXVERTS); + BitArray* to_try = get_conditions(data->to_try, 0); if (max) { // Choose a pivot with as many neighbours in as possible uint16_t pivot = 0; int16_t max_neighbours = -1; - BitArray* copy_try = new_bit_array(MAXVERTS); for (uint16_t i = 0; i < nr; i++){ if (get_bit_array(try, i) || get_bit_array(ban, i)){ - copy_bit_array(copy_try, try, nr); - intersect_bit_arrays(copy_try, GRAPH->neighbours[i], nr); - uint16_t num_neighbours = size_bit_array(copy_try, nr); + copy_bit_array(data->temp_bitarray, try, nr); + intersect_bit_arrays(data->temp_bitarray, data->graph->neighbours[i], nr); + uint16_t num_neighbours = size_bit_array(data->temp_bitarray, nr); if (num_neighbours > max_neighbours) { pivot = i; max_neighbours = num_neighbours; @@ -324,50 +327,59 @@ static void BronKerbosch(uint16_t depth, } // Try adding vertices from minus neighbours of - init_bit_array(to_try, 1, nr); - complement_bit_arrays(to_try, GRAPH->neighbours[pivot], nr); + init_bit_array(to_try, true, nr); + complement_bit_arrays(to_try, data->graph->neighbours[pivot], nr); intersect_bit_arrays(to_try, try, nr); } else { // If we are not looking for maximal cliques, a pivot cannot be used copy_bit_array(to_try, try, nr); } + // Update the height of the condition data->to_try, since we didn't use + // push_condition + data->to_try->height[0]++; // Get orbit representatives of - get_orbit_reps_bitarray(to_try, group); + get_orbit_reps_bitarray(to_try, group, data); for (uint16_t v = 0; v < nr; v++) { if (get_bit_array(to_try, v)){ - set_bit_array(CLIQUE, v, true); + set_bit_array(data->clique, v, true); - push_conditions(TRY, depth + 1, 0, GRAPH->neighbours[v]); - push_conditions(BAN, depth + 1, 0, GRAPH->neighbours[v]); + push_conditions(data->try, depth + 1, 0, data->graph->neighbours[v]); + push_conditions(data->ban, depth + 1, 0, data->graph->neighbours[v]); // recurse if (group == Fail) { - BronKerbosch(depth + 1, rep_depth, limit, nr_found, max, size, group); + if (EXIT == BronKerbosch(depth + 1, rep_depth, limit, nr_found, max, size, group, data)) { + return EXIT; + } } else { Obj stabiliser = CALL_2ARGS(Stabilizer, group, INTOBJ_INT(v + 1)); if (CALL_1ARGS(IsTrivial, stabiliser) == True) { stabiliser = Fail; } - BronKerbosch(depth + 1, rep_depth + 1, limit, nr_found, max, size, stabiliser); + if (EXIT == BronKerbosch(depth + 1, rep_depth + 1, limit, nr_found, max, size, stabiliser, data)) { + return EXIT; + } } - pop_conditions(TRY, depth + 1); - pop_conditions(BAN, depth + 1); - set_bit_array(CLIQUE, v, false); + pop_conditions(data->try, depth + 1); + pop_conditions(data->ban, depth + 1); + data->to_try->height[0]--; + set_bit_array(data->clique, v, false); if (group == Fail) { - set_bit_array(get_conditions(TRY, 0), v, false); - set_bit_array(get_conditions(BAN, 0), v, true); + set_bit_array(get_conditions(data->try, 0), v, false); + set_bit_array(get_conditions(data->ban, 0), v, true); } else { - ORBIT = CALL_2ARGS(Orbit, group, INTOBJ_INT(v + 1)); - set_bit_array_from_gap_list(ORB_LOOKUP, ORBIT); - complement_bit_arrays(get_conditions(TRY,0), ORB_LOOKUP, nr); - union_bit_arrays(get_conditions(BAN,0), ORB_LOOKUP, nr); + data->orbit = CALL_2ARGS(Orbit, group, INTOBJ_INT(v + 1)); + set_bit_array_from_gap_list(data->temp_bitarray, data->orbit); + complement_bit_arrays(get_conditions(data->try,0), data->temp_bitarray, nr); + union_bit_arrays(get_conditions(data->ban,0), data->temp_bitarray, nr); } } } + return EXIT + 1; } // FuncDigraphsCliquesFinder is the main function to use the C implementation @@ -461,9 +473,9 @@ Obj FuncDigraphsCliquesFinder(Obj self, Obj args) { for (Int i = 1; i <= LEN_LIST(include_obj); ++i) { if (!ISB_LIST(include_obj, i)) { ErrorQuit("the 5th argument must be a dense list,", 0L, 0L); - } else if (!IS_POS_INT(ELM_LIST(include_obj, i))) { + } else if (!IS_POS_INTOBJ(ELM_LIST(include_obj, i))) { ErrorQuit("the 5th argument must only contain positive " - "integers, but found %s in position %d,", + "small integers, but found %s in position %d,", (Int) TNAM_OBJ(ELM_LIST(include_obj, i)), i); } else if (INT_INTOBJ(ELM_LIST(include_obj, i)) @@ -487,9 +499,9 @@ Obj FuncDigraphsCliquesFinder(Obj self, Obj args) { for (Int i = 1; i <= LEN_LIST(exclude_obj); ++i) { if (!ISB_LIST(exclude_obj, i)) { ErrorQuit("the 6th argument must be a dense list,", 0L, 0L); - } else if (!IS_POS_INT(ELM_LIST(exclude_obj, i))) { + } else if (!IS_POS_INTOBJ(ELM_LIST(exclude_obj, i))) { ErrorQuit("the 6th argument must only contain positive " - "integers, but found %s in position %d,", + "small integers, but found %s in position %d,", (Int) TNAM_OBJ(ELM_LIST(exclude_obj, i)), i); } else if (INT_INTOBJ(ELM_LIST(exclude_obj, i)) @@ -510,17 +522,12 @@ Obj FuncDigraphsCliquesFinder(Obj self, Obj args) { (Int) TNAM_OBJ(max_obj), 0L); } - if (!IS_INTOBJ(size_obj) && size_obj != Fail) { - ErrorQuit("the 8th argument must be an integer " + if (!IS_POS_INTOBJ(size_obj) && size_obj != Fail) { + ErrorQuit("the 8th argument must be a positive small integer " "or fail, not %s,", (Int) TNAM_OBJ(size_obj), 0L); - } else if (IS_INTOBJ(size_obj) && INT_INTOBJ(size_obj) <= 0) { - ErrorQuit("the 8th argument must be a positive integer, " - "not %d,", - INT_INTOBJ(size_obj), - 0L); - } + } if (aut_grp_obj != Fail) { if (CALL_1ARGS(IsPermGroup, aut_grp_obj) != True) { @@ -531,8 +538,6 @@ Obj FuncDigraphsCliquesFinder(Obj self, Obj args) { 0L); } Obj gens = CALL_1ARGS(GeneratorsOfGroup, aut_grp_obj); - DIGRAPHS_ASSERT(IS_LIST(gens)); - DIGRAPHS_ASSERT(LEN_LIST(gens) > 0); UInt lmp = INT_INTOBJ(CALL_1ARGS(LargestMovedPointPerms, gens)); if (lmp > 0 && LEN_LIST(gens) >= lmp) { ErrorQuit("expected at most %d generators in the 9th argument " @@ -541,7 +546,7 @@ Obj FuncDigraphsCliquesFinder(Obj self, Obj args) { LEN_LIST(gens)); } for (UInt i = 1; i <= LEN_LIST(gens); ++i) { - if (CALL_2ARGS(IsDigraphAutomorphism, digraph_obj, ELM_LIST(gens, i)) + if (CALL_2ARGS(IsDigraphAutomorphism, digraph_obj, ELM_LIST(gens, i)) != True) { ErrorQuit("expected group of automorphisms, but found a " "non-automorphism in position %d of the group generators,", @@ -603,6 +608,8 @@ Obj FuncDigraphsCliquesFinder(Obj self, Obj args) { uint64_t nr_found = 0; uint64_t limit = (limit_obj == Infinity ? SMALLINTLIMIT : INT_INTOBJ(limit_obj)); bool max = (max_obj == True ? true : false); + + static CliqueData data = {}; // Initialise all the variable which will be used to carry out the recursion if (!init_data_from_args(digraph_obj, hook_obj, @@ -610,19 +617,18 @@ Obj FuncDigraphsCliquesFinder(Obj self, Obj args) { include_obj, exclude_obj, max_obj, - &aut_grp_obj)) { + &aut_grp_obj, + &data)) { return user_param_obj; } // The clique we are trying to extend is already big enough if (size != 0 && include_size == size) { - HOOK(USER_PARAM, CLIQUE, nr); + data.hook(data.user_param, data.clique, nr, data.gap_func); return user_param_obj; } // go! - if (setjmp(OUTOFHERE) == 0) { - BronKerbosch(0, 0, limit, &nr_found, max, (size == 0 ? size : size - include_size), aut_grp_obj); - } + BronKerbosch(0, 0, limit, &nr_found, max, (size == 0 ? size : size - include_size), aut_grp_obj, &data); return user_param_obj; } From 7cc4ab2f9bf850f6c2f6ae88fab4fd15cd2f97b9 Mon Sep 17 00:00:00 2001 From: Julius Date: Fri, 7 Feb 2020 15:11:13 +0100 Subject: [PATCH 13/30] add: initial set up for implementing Bron Kerbosch algorithm --- Makefile.am | 2 + src/cliques.c | 279 +++++++++++++++++++++++++++++++++++++++++++++++++ src/cliques.h | 19 ++++ src/digraphs.c | 8 ++ 4 files changed, 308 insertions(+) create mode 100644 src/cliques.c create mode 100644 src/cliques.h diff --git a/Makefile.am b/Makefile.am index 2f6ad4161..06280c99e 100644 --- a/Makefile.am +++ b/Makefile.am @@ -27,6 +27,7 @@ pkginclude_HEADERS += src/digraphs-debug.h pkginclude_HEADERS += src/digraphs.h pkginclude_HEADERS += src/homos-graphs.h pkginclude_HEADERS += src/homos.h +pkginclude_HEADERS += src/cliques.h pkginclude_HEADERS += src/perms.h pkginclude_HEADERS += src/planar.h pkginclude_HEADERS += src/schreier-sims.h @@ -50,6 +51,7 @@ digraphs_la_SOURCES = src/digraphs.c digraphs_la_SOURCES += src/bitarray.c digraphs_la_SOURCES += src/conditions.c digraphs_la_SOURCES += src/homos.c +digraphs_la_SOURCES += src/cliques.c digraphs_la_SOURCES += src/homos-graphs.c digraphs_la_SOURCES += src/perms.c digraphs_la_SOURCES += src/planar.c diff --git a/src/cliques.c b/src/cliques.c new file mode 100644 index 000000000..d7218f0c5 --- /dev/null +++ b/src/cliques.c @@ -0,0 +1,279 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// cliques.c di/cliques Julius Jonusas +// +// Copyright (C) 2020 - Julius Jonusas +// +// This file is free software, see the digraphs/LICENSE. + + +// C headers +#include // for CHAR_BIT +#include // for longjmp, setjmp, jmp_buf +#include // for true, false, bool +#include // for NULL +#include // for uint16_t, uint64_t +#include // for malloc, NULL +#include // for time + +// GAP headers +#include "src/compiled.h" + +// Digraphs package headers +#include "bitarray.h" // for BitArray +#include "conditions.h" // for Conditions +#include "digraphs-config.h" // for DIGRAPHS_HAVE___BUILTIN_CTZLL +#include "digraphs-debug.h" // for DIGRAPHS_ASSERT +#include "homos-graphs.h" // for Digraph, Graph, . . . +#include "perms.h" // for MAXVERTS, UNDEFINED, PermColl, Perm +#include "schreier-sims.h" // for PermColl, . . . +#include "cliques.h" + +//////////////////////////////////////////////////////////////////////////////// +// Forward declarations +//////////////////////////////////////////////////////////////////////////////// + +// Defined in digraphs.h +Int DigraphNrVertices(Obj); +Obj FuncOutNeighbours(Obj, Obj); + +// GAP level things, imported in digraphs.c +extern Obj IsDigraph; +extern Obj DIGRAPHS_ValidateVertexColouring; +extern Obj Infinity; +extern Obj IsSymmetricDigraph; +extern Obj GeneratorsOfGroup; +extern Obj AutomorphismGroup; +extern Obj IsPermGroup; +extern Obj IsDigraphAutomorphism; +extern Obj LargestMovedPointPerms; + +//////////////////////////////////////////////////////////////////////////////// +// Global variables +//////////////////////////////////////////////////////////////////////////////// + +static Obj GAP_FUNC; // Variable to hold a GAP level hook function + +static Obj (*HOOK)(void*, // HOOK function applied to every homo found + const uint16_t, + const uint8_t*); +static void* USER_PARAM; // a USER_PARAM for the hook + +// Values in MAP are restricted to those positions in IMAGE_RESTRICT +static jmp_buf OUTOFHERE; // so we can jump out of the deepest + + +static Digraph* DIGRAPH; // Digraphs to hold incoming GAP digraphs + +static Graph* GRAPH; // Graphs to hold incoming GAP symmetric digraphs + + +//////////////////////////////////////////////////////////////////////////////// +// +//////////////////////////////////////////////////////////////////////////////// + +static void BronKerbosch() { + + // TODO + +} + + +static Obj clique_hook_collect(void* user_param, uint16_t const nr, uint8_t const* clique) { + UInt2* ptr; + UInt i; + Obj c; + + if (TNUM_OBJ((Obj) user_param) == T_PLIST_EMPTY) { + RetypeBag(user_param, T_PLIST); + } + + c = NewEmptyPlist(); + for(i = 0; i < nr; i++) { + PushPlist(c, ObjInt_UInt8(clique[i])); + } + + ASS_LIST(user_param, LEN_LIST(user_param) + 1, c); + return False; +} + +static Obj clique_hook_gap(void* user_param, uint16_t const nr, uint8_t const* clique) { + UInt2* ptr; + UInt i; + Obj c; + + c = NewEmptyPlist(); + for(i = 0; i < nr; i++) { + PushPlist(c, ObjInt_UInt8(clique[i])); + } + + return CALL_2ARGS(GAP_FUNC, user_param, c); +} + + +static void init_clique_graph_from_digraph_obj(Graph* const graph, + Obj digraph_obj) { + DIGRAPHS_ASSERT(graph != NULL); + DIGRAPHS_ASSERT(CALL_1ARGS(IsDigraph, digraph_obj) == True); + DIGRAPHS_ASSERT(CALL_1ARGS(IsSymmetricDigraph, digraph_obj) == True); + UInt const nr = DigraphNrVertices(digraph_obj); + Obj out = FuncOutNeighbours(0L, digraph_obj); + DIGRAPHS_ASSERT(nr < MAXVERTS); + DIGRAPHS_ASSERT(IS_PLIST(out)); + clear_graph(graph, nr); + + for (uint16_t i = 1; i <= nr; i++) { + Obj nbs = ELM_PLIST(out, i); + DIGRAPHS_ASSERT(IS_LIST(nbs)); + for (uint16_t j = 1; j <= LEN_LIST(nbs); j++) { + DIGRAPHS_ASSERT(IS_INTOBJ(ELM_LIST(nbs, j))); + add_edge_graph(graph, i - 1, INT_INTOBJ(ELM_LIST(nbs, j)) - 1); + } + } +} + +static void init_clique_digraph_from_digraph_obj(Digraph* const digraph, + Obj digraph_obj) { + DIGRAPHS_ASSERT(digraph != NULL); + DIGRAPHS_ASSERT(CALL_1ARGS(IsDigraph, digraph_obj) == True); + UInt const nr = DigraphNrVertices(digraph_obj); + Obj out = FuncOutNeighbours(0L, digraph_obj); + DIGRAPHS_ASSERT(nr < MAXVERTS); + DIGRAPHS_ASSERT(IS_PLIST(out)); + clear_digraph(digraph, nr); + + for (uint16_t i = 1; i <= nr; i++) { + Obj nbs = ELM_PLIST(out, i); + DIGRAPHS_ASSERT(IS_LIST(nbs)); + for (uint16_t j = 1; j <= LEN_LIST(nbs); j++) { + DIGRAPHS_ASSERT(IS_INTOBJ(ELM_LIST(nbs, j))); + add_edge_digraph(digraph, i - 1, INT_INTOBJ(ELM_LIST(nbs, j)) - 1); + } + } +} + +static bool init_clique_data_from_args(Obj digraph_obj, + Obj hook_obj, + Obj user_param_obj, + Obj limit_obj, + Obj include_obj, + Obj exclude_obj, + Obj max_obj, + Obj size_obj) { + static bool is_initialized = false; + if (!is_initialized) { + is_initialized = true; + + DIGRAPH = new_digraph(MAXVERTS); + + GRAPH = new_graph(MAXVERTS); + } + + uint16_t nr = DigraphNrVertices(digraph_obj); + + bool is_undirected; + if (CALL_1ARGS(IsSymmetricDigraph, digraph_obj) == True) { + init_clique_graph_from_digraph_obj(GRAPH, digraph_obj); + is_undirected = true; + } else { + init_clique_digraph_from_digraph_obj(DIGRAPH, digraph_obj); + is_undirected = false; + } + + if (hook_obj != Fail) { + GAP_FUNC = hook_obj; + HOOK = clique_hook_gap; + } else { + HOOK = clique_hook_collect; + } + USER_PARAM = user_param_obj; + + return true; +} + +Obj FuncDigraphsCliqueFinder(Obj self, Obj args) { + if (LEN_PLIST(args) != 8) { + ErrorQuit( + "there must be 8 arguments, found %d,", LEN_PLIST(args), 0L); + } + Obj digraph_obj = ELM_PLIST(args, 1); + Obj hook_obj = ELM_PLIST(args, 2); + Obj user_param_obj = ELM_PLIST(args, 3); + Obj limit_obj = ELM_PLIST(args, 4); + Obj include_obj = ELM_PLIST(args, 5); + Obj exclude_obj = ELM_PLIST(args, 6); + Obj max_obj = ELM_PLIST(args, 7); + Obj size_obj = ELM_PLIST(args, 8); + + // Validate the arguments + if (CALL_1ARGS(IsDigraph, digraph_obj) != True) { + ErrorQuit("the 1st argument must be a digraph, not %s,", + (Int) TNAM_OBJ(digraph_obj), + 0L); + } else if (DigraphNrVertices(digraph_obj) > MAXVERTS) { + ErrorQuit("the 1st argument must have at most 512 vertices, " + "found %d,", + DigraphNrVertices(digraph_obj), + 0L); + } + if (hook_obj == Fail) { + if (!IS_LIST(user_param_obj) || !IS_MUTABLE_OBJ(user_param_obj)) { + ErrorQuit("the 2rd argument is fail and so the 3th argument must " + "be a mutable list, not %s,", + (Int) TNAM_OBJ(user_param_obj), + 0L); + } + } else if (!IS_FUNC(hook_obj) || NARG_FUNC(hook_obj) != 2) { + ErrorQuit( + "the 2rd argument must be a function with 2 arguments,", 0L, 0L); + } + if (!IS_INTOBJ(limit_obj) && limit_obj != Fail) { + ErrorQuit("the 4th argument must be an integer " + "or fail, not %s,", + (Int) TNAM_OBJ(limit_obj), + 0L); + } else if (IS_INTOBJ(limit_obj) && INT_INTOBJ(limit_obj) <= 0) { + ErrorQuit("the 4th argument must be a positive integer, " + "not %d,", + INT_INTOBJ(limit_obj), + 0L); + } + if (max_obj != True && max_obj != False) { + ErrorQuit("the 7th argument must true or false, not %s,", + (Int) TNAM_OBJ(max_obj), + 0L); + } + if (!IS_INTOBJ(size_obj) && size_obj != Fail) { + ErrorQuit("the 8th argument must be an integer " + "or fail, not %s,", + (Int) TNAM_OBJ(size_obj), + 0L); + } else if (IS_INTOBJ(size_obj) && INT_INTOBJ(size_obj) <= 0) { + ErrorQuit("the 8th argument must be a positive integer, " + "not %d,", + INT_INTOBJ(size_obj), + 0L); + } + + // Initialise all the variable which will be used to carry out the recursion + if (!init_clique_data_from_args(digraph_obj, + hook_obj, + user_param_obj, + limit_obj, + include_obj, + exclude_obj, + max_obj, + size_obj)) { + return user_param_obj; + } + + + // go! + if (setjmp(OUTOFHERE) == 0) { + BronKerbosch(); + } + + return user_param_obj; +} + + diff --git a/src/cliques.h b/src/cliques.h new file mode 100644 index 000000000..c8aa5581d --- /dev/null +++ b/src/cliques.h @@ -0,0 +1,19 @@ +/******************************************************************************** +** +*A cliques.h di/cliques Julius Jonusas +** +** Copyright (C) 2020 - Julius Jonusas +** +** This file is free software, see the digraphs/LICENSE. +** +********************************************************************************/ + +#ifndef DIGRAPHS_SRC_CLIQUES_H_ +#define DIGRAPHS_SRC_CLIQUES_H_ + +// GAP headers +#include "src/compiled.h" + +Obj FuncDigraphsCliqueFinder(Obj self, Obj args); + +#endif // DIGRAPHS_SRC_CLIQUES_H_ diff --git a/src/digraphs.c b/src/digraphs.c index c5203c751..ac0394632 100644 --- a/src/digraphs.c +++ b/src/digraphs.c @@ -21,6 +21,7 @@ #include "digraphs-debug.h" // for DIGRAPHS_ASSERT #include "homos.h" // for FuncHomomorphismDigraphsFinder +#include "cliques.h" #include "planar.h" // for FUNC_IS_PLANAR, . . . #ifdef DIGRAPHS_WITH_INCLUDED_BLISS @@ -2250,6 +2251,13 @@ static StructGVarFunc GVarFuncs[] = { FuncHomomorphismDigraphsFinder, "src/homos.c:FuncHomomorphismDigraphsFinder"}, + {"DigraphsCliqueFinder", + -1, + "digraph, hook, user_param, limit, include, " + "exclude, max, size", + FuncDigraphsCliqueFinder, + "src/cliques.c:FuncDigraphsCliqueFinder"}, + {"IS_PLANAR", 1, "digraph", FuncIS_PLANAR, "src/planar.c:FuncIS_PLANAR"}, {"PLANAR_EMBEDDING", From 7ba60bfcd632f879829a1e59c068eb9b7ecf81f5 Mon Sep 17 00:00:00 2001 From: Julius Date: Tue, 3 Mar 2020 16:46:33 +0100 Subject: [PATCH 14/30] add: implemented BronKerbosch with pivoting --- src/cliques.c | 78 ++++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 62 insertions(+), 16 deletions(-) diff --git a/src/cliques.c b/src/cliques.c index d7218f0c5..003349874 100644 --- a/src/cliques.c +++ b/src/cliques.c @@ -55,8 +55,8 @@ extern Obj LargestMovedPointPerms; static Obj GAP_FUNC; // Variable to hold a GAP level hook function static Obj (*HOOK)(void*, // HOOK function applied to every homo found - const uint16_t, - const uint8_t*); + const BitArray*, + const uint16_t); static void* USER_PARAM; // a USER_PARAM for the hook // Values in MAP are restricted to those positions in IMAGE_RESTRICT @@ -67,20 +67,51 @@ static Digraph* DIGRAPH; // Digraphs to hold incoming GAP digraphs static Graph* GRAPH; // Graphs to hold incoming GAP symmetric digraphs +static BitArray* CLIQUE; +static Conditions* TRY; +static Conditions* BAN; + //////////////////////////////////////////////////////////////////////////////// // //////////////////////////////////////////////////////////////////////////////// -static void BronKerbosch() { - - // TODO +static void BronKerbosch(uint16_t depth) { + + uint16_t nr = GRAPH->nr_vertices; + + if (size_bit_array(get_conditions(TRY, 0), nr) == 0 && size_bit_array(get_conditions(BAN,0), nr) ==0) { + // is a maximal clique + HOOK(USER_PARAM, CLIQUE, nr); + return; + } + + BitArray* try = get_conditions(TRY, 0); + for (uint16_t i = 0; i < nr; i++) { + if (get_bit_array(try, i)){ + set_bit_array(CLIQUE, i, true); + + + push_conditions(TRY, depth + 1, 0, GRAPH->neighbours[i]); + push_conditions(BAN, depth + 1, 0, GRAPH->neighbours[i]); + + // recurse + BronKerbosch(depth + 1); + + pop_conditions(TRY, depth + 1); + pop_conditions(BAN, depth + 1); + set_bit_array(CLIQUE, i, false); + + set_bit_array(get_conditions(TRY, 0), i , false); + set_bit_array(get_conditions(BAN, 0), i , true); + + } + } } -static Obj clique_hook_collect(void* user_param, uint16_t const nr, uint8_t const* clique) { - UInt2* ptr; +static Obj clique_hook_collect(void* user_param, const BitArray* clique, const uint16_t nr) { UInt i; Obj c; @@ -88,23 +119,26 @@ static Obj clique_hook_collect(void* user_param, uint16_t const nr, uint8_t cons RetypeBag(user_param, T_PLIST); } - c = NewEmptyPlist(); - for(i = 0; i < nr; i++) { - PushPlist(c, ObjInt_UInt8(clique[i])); + c = NEW_PLIST(T_PLIST, nr); + for(i = 1; i <= nr; i++) { + if (get_bit_array(clique, i - 1)){ + PushPlist(c, ObjInt_UInt(i)); + } } ASS_LIST(user_param, LEN_LIST(user_param) + 1, c); return False; } -static Obj clique_hook_gap(void* user_param, uint16_t const nr, uint8_t const* clique) { - UInt2* ptr; +static Obj clique_hook_gap(void* user_param, const BitArray* clique, const uint16_t nr) { UInt i; Obj c; - c = NewEmptyPlist(); - for(i = 0; i < nr; i++) { - PushPlist(c, ObjInt_UInt8(clique[i])); + c = NEW_PLIST(T_PLIST, nr); + for(i = 1; i <= nr; i++) { + if (get_bit_array(clique, i-1)) { + PushPlist(c, ObjInt_UInt(i + 1)); + } } return CALL_2ARGS(GAP_FUNC, user_param, c); @@ -167,9 +201,20 @@ static bool init_clique_data_from_args(Obj digraph_obj, DIGRAPH = new_digraph(MAXVERTS); GRAPH = new_graph(MAXVERTS); + + // Currently Conditions are a nr1 x nr1 array of BitArrays, so both + // values have to be set to MAXVERTS + CLIQUE = new_bit_array(MAXVERTS); + TRY = new_conditions(MAXVERTS, MAXVERTS); + BAN = new_conditions(MAXVERTS, MAXVERTS); } uint16_t nr = DigraphNrVertices(digraph_obj); + init_bit_array(CLIQUE, false, nr); + clear_conditions(TRY, nr + 1, nr); + clear_conditions(BAN, nr + 1, nr); + init_bit_array(BAN->bit_array[0], false, nr); + bool is_undirected; if (CALL_1ARGS(IsSymmetricDigraph, digraph_obj) == True) { @@ -269,8 +314,9 @@ Obj FuncDigraphsCliqueFinder(Obj self, Obj args) { // go! + if (setjmp(OUTOFHERE) == 0) { - BronKerbosch(); + BronKerbosch(0); } return user_param_obj; From 0d732c8de332eef9f559109d9b084d2712e8f56a Mon Sep 17 00:00:00 2001 From: Julius Date: Thu, 19 Mar 2020 15:40:32 +0100 Subject: [PATCH 15/30] add: BronKerboch now uses a pivot --- src/cliques.c | 31 +++++++++++++++++++++++++------ 1 file changed, 25 insertions(+), 6 deletions(-) diff --git a/src/cliques.c b/src/cliques.c index 003349874..28d9c0c5b 100644 --- a/src/cliques.c +++ b/src/cliques.c @@ -79,19 +79,40 @@ static Conditions* BAN; static void BronKerbosch(uint16_t depth) { uint16_t nr = GRAPH->nr_vertices; + BitArray* try = get_conditions(TRY, 0); + BitArray* ban = get_conditions(BAN, 0); - if (size_bit_array(get_conditions(TRY, 0), nr) == 0 && size_bit_array(get_conditions(BAN,0), nr) ==0) { + if (size_bit_array(try, nr) == 0 && size_bit_array(ban, nr) ==0) { // is a maximal clique HOOK(USER_PARAM, CLIQUE, nr); return; } - BitArray* try = get_conditions(TRY, 0); + // Choose a pivot with as a many neighbours in as possible + uint16_t pivot = 0; + int max_neighbours = -1; + for (uint16_t i = 0; i < nr; i++){ + if (get_bit_array(try, i) || get_bit_array(ban, i)){ + BitArray* copy_try = new_bit_array(MAXVERTS); // reuse it! + copy_bit_array(copy_try, try, nr); + intersect_bit_arrays(copy_try, GRAPH->neighbours[i], nr); + uint16_t num_neighbours = size_bit_array(copy_try, nr); + if (num_neighbours > max_neighbours) { + pivot = i; + max_neighbours = num_neighbours; + } + } + } + + // Try adding vertices from minus neighbours of + BitArray* to_try = new_bit_array(MAXVERTS); + init_bit_array(to_try, 1, nr); + complement_bit_arrays(to_try, GRAPH->neighbours[pivot], nr); + intersect_bit_arrays(to_try, try, nr); for (uint16_t i = 0; i < nr; i++) { - if (get_bit_array(try, i)){ + if (get_bit_array(to_try, i)){ set_bit_array(CLIQUE, i, true); - push_conditions(TRY, depth + 1, 0, GRAPH->neighbours[i]); push_conditions(BAN, depth + 1, 0, GRAPH->neighbours[i]); @@ -103,9 +124,7 @@ static void BronKerbosch(uint16_t depth) { set_bit_array(CLIQUE, i, false); set_bit_array(get_conditions(TRY, 0), i , false); - set_bit_array(get_conditions(BAN, 0), i , true); - } } } From bbb70b209cd0b73e239544e8c512be0fd22f469f Mon Sep 17 00:00:00 2001 From: Julius Date: Thu, 19 Mar 2020 17:32:33 +0100 Subject: [PATCH 16/30] add: only use the symmetric part of the graph in the search cliques --- src/cliques.c | 52 +++++++++++++-------------------------------------- 1 file changed, 13 insertions(+), 39 deletions(-) diff --git a/src/cliques.c b/src/cliques.c index 28d9c0c5b..198114d1d 100644 --- a/src/cliques.c +++ b/src/cliques.c @@ -36,6 +36,7 @@ // Defined in digraphs.h Int DigraphNrVertices(Obj); Obj FuncOutNeighbours(Obj, Obj); +Obj FuncADJACENCY_MATRIX(Obj, Obj); // GAP level things, imported in digraphs.c extern Obj IsDigraph; @@ -62,9 +63,6 @@ static void* USER_PARAM; // a USER_PARAM for the hook // Values in MAP are restricted to those positions in IMAGE_RESTRICT static jmp_buf OUTOFHERE; // so we can jump out of the deepest - -static Digraph* DIGRAPH; // Digraphs to hold incoming GAP digraphs - static Graph* GRAPH; // Graphs to hold incoming GAP symmetric digraphs static BitArray* CLIQUE; @@ -168,39 +166,23 @@ static void init_clique_graph_from_digraph_obj(Graph* const graph, Obj digraph_obj) { DIGRAPHS_ASSERT(graph != NULL); DIGRAPHS_ASSERT(CALL_1ARGS(IsDigraph, digraph_obj) == True); - DIGRAPHS_ASSERT(CALL_1ARGS(IsSymmetricDigraph, digraph_obj) == True); UInt const nr = DigraphNrVertices(digraph_obj); Obj out = FuncOutNeighbours(0L, digraph_obj); + Obj adj_mat = FuncADJACENCY_MATRIX(0L, digraph_obj); DIGRAPHS_ASSERT(nr < MAXVERTS); DIGRAPHS_ASSERT(IS_PLIST(out)); clear_graph(graph, nr); - for (uint16_t i = 1; i <= nr; i++) { - Obj nbs = ELM_PLIST(out, i); - DIGRAPHS_ASSERT(IS_LIST(nbs)); - for (uint16_t j = 1; j <= LEN_LIST(nbs); j++) { - DIGRAPHS_ASSERT(IS_INTOBJ(ELM_LIST(nbs, j))); - add_edge_graph(graph, i - 1, INT_INTOBJ(ELM_LIST(nbs, j)) - 1); - } - } -} - -static void init_clique_digraph_from_digraph_obj(Digraph* const digraph, - Obj digraph_obj) { - DIGRAPHS_ASSERT(digraph != NULL); - DIGRAPHS_ASSERT(CALL_1ARGS(IsDigraph, digraph_obj) == True); - UInt const nr = DigraphNrVertices(digraph_obj); - Obj out = FuncOutNeighbours(0L, digraph_obj); - DIGRAPHS_ASSERT(nr < MAXVERTS); - DIGRAPHS_ASSERT(IS_PLIST(out)); - clear_digraph(digraph, nr); + + // Only include symmetric edges for (uint16_t i = 1; i <= nr; i++) { - Obj nbs = ELM_PLIST(out, i); - DIGRAPHS_ASSERT(IS_LIST(nbs)); - for (uint16_t j = 1; j <= LEN_LIST(nbs); j++) { - DIGRAPHS_ASSERT(IS_INTOBJ(ELM_LIST(nbs, j))); - add_edge_digraph(digraph, i - 1, INT_INTOBJ(ELM_LIST(nbs, j)) - 1); + Obj row = ELM_PLIST(adj_mat, i); + DIGRAPHS_ASSERT(IS_LIST(row)); + for (uint16_t j = 1; j <= nr; j++) { + if (ELM_PLIST(row, j) != INTOBJ_INT(0) && ELM_PLIST(ELM_PLIST(adj_mat,j) ,i) != INTOBJ_INT(0)){ + add_edge_graph(graph, i - 1, j - 1); + } } } } @@ -217,8 +199,6 @@ static bool init_clique_data_from_args(Obj digraph_obj, if (!is_initialized) { is_initialized = true; - DIGRAPH = new_digraph(MAXVERTS); - GRAPH = new_graph(MAXVERTS); // Currently Conditions are a nr1 x nr1 array of BitArrays, so both @@ -235,14 +215,8 @@ static bool init_clique_data_from_args(Obj digraph_obj, init_bit_array(BAN->bit_array[0], false, nr); - bool is_undirected; - if (CALL_1ARGS(IsSymmetricDigraph, digraph_obj) == True) { - init_clique_graph_from_digraph_obj(GRAPH, digraph_obj); - is_undirected = true; - } else { - init_clique_digraph_from_digraph_obj(DIGRAPH, digraph_obj); - is_undirected = false; - } + // TODO: deal with non-symmetricity + init_clique_graph_from_digraph_obj(GRAPH, digraph_obj); if (hook_obj != Fail) { GAP_FUNC = hook_obj; @@ -269,7 +243,7 @@ Obj FuncDigraphsCliqueFinder(Obj self, Obj args) { Obj max_obj = ELM_PLIST(args, 7); Obj size_obj = ELM_PLIST(args, 8); - // Validate the arguments + // Validate the arguments if (CALL_1ARGS(IsDigraph, digraph_obj) != True) { ErrorQuit("the 1st argument must be a digraph, not %s,", (Int) TNAM_OBJ(digraph_obj), From 110cc14d5f5e51b1e523fbc52f9d87c3b6fe4595 Mon Sep 17 00:00:00 2001 From: Julius Date: Wed, 25 Mar 2020 15:50:34 +0100 Subject: [PATCH 17/30] add: implemented the use of the addional parameters -- limit, max, size. --- src/cliques.c | 86 ++++++++++++++++++++++++++++++++------------------- 1 file changed, 54 insertions(+), 32 deletions(-) diff --git a/src/cliques.c b/src/cliques.c index 198114d1d..ad73af013 100644 --- a/src/cliques.c +++ b/src/cliques.c @@ -69,44 +69,66 @@ static BitArray* CLIQUE; static Conditions* TRY; static Conditions* BAN; - //////////////////////////////////////////////////////////////////////////////// // //////////////////////////////////////////////////////////////////////////////// -static void BronKerbosch(uint16_t depth) { +static void BronKerbosch(uint16_t depth, + uint16_t limit, + uint16_t* nr_found, + bool max, + uint16_t size) { uint16_t nr = GRAPH->nr_vertices; BitArray* try = get_conditions(TRY, 0); BitArray* ban = get_conditions(BAN, 0); - if (size_bit_array(try, nr) == 0 && size_bit_array(ban, nr) ==0) { - // is a maximal clique + if (depth > 0 && !max && ( size == 0 || size == depth)) { + // We are not looking for maximal cliques HOOK(USER_PARAM, CLIQUE, nr); + *nr_found += 1; + if (*nr_found >= limit) { + longjmp(OUTOFHERE, 1); + } + } else if (size_bit_array(try, nr) == 0 && size_bit_array(ban, nr) == 0) { + // is a maximal clique + if (size == 0 || size == depth) { + HOOK(USER_PARAM, CLIQUE, nr); + *nr_found += 1; + if (*nr_found >= limit) { + longjmp(OUTOFHERE, 1); + } + } return; } - // Choose a pivot with as a many neighbours in as possible - uint16_t pivot = 0; - int max_neighbours = -1; - for (uint16_t i = 0; i < nr; i++){ - if (get_bit_array(try, i) || get_bit_array(ban, i)){ - BitArray* copy_try = new_bit_array(MAXVERTS); // reuse it! - copy_bit_array(copy_try, try, nr); - intersect_bit_arrays(copy_try, GRAPH->neighbours[i], nr); - uint16_t num_neighbours = size_bit_array(copy_try, nr); - if (num_neighbours > max_neighbours) { - pivot = i; - max_neighbours = num_neighbours; + BitArray* to_try = new_bit_array(MAXVERTS); + if (max) { + // Choose a pivot with as a many neighbours in as possible + uint16_t pivot = 0; + int max_neighbours = -1; + for (uint16_t i = 0; i < nr; i++){ + if (get_bit_array(try, i) || get_bit_array(ban, i)){ + BitArray* copy_try = new_bit_array(MAXVERTS); // reuse it! + copy_bit_array(copy_try, try, nr); + intersect_bit_arrays(copy_try, GRAPH->neighbours[i], nr); + uint16_t num_neighbours = size_bit_array(copy_try, nr); + if (num_neighbours > max_neighbours) { + pivot = i; + max_neighbours = num_neighbours; + } } } + + // Try adding vertices from minus neighbours of + init_bit_array(to_try, 1, nr); + complement_bit_arrays(to_try, GRAPH->neighbours[pivot], nr); + intersect_bit_arrays(to_try, try, nr); + } else { + // If we are not looking for maximal cliques, a pivot cannot be used + copy_bit_array(to_try, try, nr); } - // Try adding vertices from minus neighbours of - BitArray* to_try = new_bit_array(MAXVERTS); - init_bit_array(to_try, 1, nr); - complement_bit_arrays(to_try, GRAPH->neighbours[pivot], nr); - intersect_bit_arrays(to_try, try, nr); for (uint16_t i = 0; i < nr; i++) { if (get_bit_array(to_try, i)){ set_bit_array(CLIQUE, i, true); @@ -115,7 +137,7 @@ static void BronKerbosch(uint16_t depth) { push_conditions(BAN, depth + 1, 0, GRAPH->neighbours[i]); // recurse - BronKerbosch(depth + 1); + BronKerbosch(depth + 1, limit, nr_found, max, size); pop_conditions(TRY, depth + 1); pop_conditions(BAN, depth + 1); @@ -190,7 +212,6 @@ static void init_clique_graph_from_digraph_obj(Graph* const graph, static bool init_clique_data_from_args(Obj digraph_obj, Obj hook_obj, Obj user_param_obj, - Obj limit_obj, Obj include_obj, Obj exclude_obj, Obj max_obj, @@ -214,8 +235,6 @@ static bool init_clique_data_from_args(Obj digraph_obj, clear_conditions(BAN, nr + 1, nr); init_bit_array(BAN->bit_array[0], false, nr); - - // TODO: deal with non-symmetricity init_clique_graph_from_digraph_obj(GRAPH, digraph_obj); if (hook_obj != Fail) { @@ -265,9 +284,9 @@ Obj FuncDigraphsCliqueFinder(Obj self, Obj args) { ErrorQuit( "the 2rd argument must be a function with 2 arguments,", 0L, 0L); } - if (!IS_INTOBJ(limit_obj) && limit_obj != Fail) { + if (!IS_INTOBJ(limit_obj) && limit_obj != Infinity) { ErrorQuit("the 4th argument must be an integer " - "or fail, not %s,", + "or infinity, not %s,", (Int) TNAM_OBJ(limit_obj), 0L); } else if (IS_INTOBJ(limit_obj) && INT_INTOBJ(limit_obj) <= 0) { @@ -297,7 +316,6 @@ Obj FuncDigraphsCliqueFinder(Obj self, Obj args) { if (!init_clique_data_from_args(digraph_obj, hook_obj, user_param_obj, - limit_obj, include_obj, exclude_obj, max_obj, @@ -305,14 +323,18 @@ Obj FuncDigraphsCliqueFinder(Obj self, Obj args) { return user_param_obj; } + uint16_t nr_found = 0; + uint16_t limit = (limit_obj == Infinity ? MAXVERTS : INT_INTOBJ(limit_obj)); + uint16_t size = (size_obj == Fail ? 0 : INT_INTOBJ(size_obj)); + bool max = (max_obj == True ? true : false); + if (size > GRAPH->nr_vertices) { + return user_param_obj; + } // go! - if (setjmp(OUTOFHERE) == 0) { - BronKerbosch(0); + BronKerbosch(0, limit, &nr_found, max, size); } return user_param_obj; } - - From b4776b2604f31a77c47432a2879a843651a9e119 Mon Sep 17 00:00:00 2001 From: Julius Date: Fri, 3 Apr 2020 16:32:21 +0200 Subject: [PATCH 18/30] add: using the automorphism group in the computation --- src/bitarray.h | 17 ++++++ src/cliques.c | 161 +++++++++++++++++++++++++++++++++++++++++++------ src/homos.c | 4 +- 3 files changed, 162 insertions(+), 20 deletions(-) diff --git a/src/bitarray.h b/src/bitarray.h index fa8d554ed..2bea36a11 100644 --- a/src/bitarray.h +++ b/src/bitarray.h @@ -339,6 +339,23 @@ static inline void intersect_bit_arrays(BitArray* const bit_array1, } } +//! Union the BitArray's pointed to by \p bit_array1 and \p bit_array2. The +//! BitArray pointed to by \p bit_array1 is changed in place! +static inline void union_bit_arrays(BitArray* const bit_array1, + BitArray const* const bit_array2, + uint16_t const nr_bits) { + DIGRAPHS_ASSERT(bit_array1 != NULL); + DIGRAPHS_ASSERT(bit_array2 != NULL); + DIGRAPHS_ASSERT(bit_array1->nr_bits == bit_array2->nr_bits); + DIGRAPHS_ASSERT(bit_array1->nr_blocks == bit_array2->nr_blocks); + DIGRAPHS_ASSERT(nr_bits <= bit_array1->nr_bits); + DIGRAPHS_ASSERT(nr_bits <= bit_array2->nr_bits); + uint16_t const nr_blocks = NR_BLOCKS_LOOKUP[nr_bits]; + for (uint16_t i = 0; i < nr_blocks; i++) { + bit_array1->blocks[i] |= bit_array2->blocks[i]; + } +} + //! Sets \p bit_array1 to be 0 in every position that \p bit_array2 is 1. static inline void complement_bit_arrays(BitArray* const bit_array1, BitArray const* const bit_array2, diff --git a/src/cliques.c b/src/cliques.c index ad73af013..25746d909 100644 --- a/src/cliques.c +++ b/src/cliques.c @@ -38,6 +38,10 @@ Int DigraphNrVertices(Obj); Obj FuncOutNeighbours(Obj, Obj); Obj FuncADJACENCY_MATRIX(Obj, Obj); +// Defined in homos.c +void set_automorphisms(Obj, PermColl*); +void get_automorphism_group_from_gap(Obj, PermColl*); + // GAP level things, imported in digraphs.c extern Obj IsDigraph; extern Obj DIGRAPHS_ValidateVertexColouring; @@ -69,11 +73,52 @@ static BitArray* CLIQUE; static Conditions* TRY; static Conditions* BAN; +static uint16_t ORB[MAXVERTS]; // Array for containing nodes in an orbit. +static BitArray* ORB_LOOKUP; // points in orbit +static PermColl* STAB_GENS[MAXVERTS]; // stabiliser generators +static SchreierSims* SCHREIER_SIMS; + //////////////////////////////////////////////////////////////////////////////// // //////////////////////////////////////////////////////////////////////////////// +// Update a BitArray to only include one vertex per orbit of a group generated +// by STAB_GENS[rep_depth] +static void get_orbit_reps_bitarray(BitArray* bit_array, uint16_t const rep_depth) { + if (STAB_GENS[rep_depth]->size == 0) { + return; + } + + uint16_t nr = GRAPH->nr_vertices; + uint16_t pt = 0; + init_bit_array(ORB_LOOKUP, false, nr); + while (pt < nr) { + if (get_bit_array(bit_array, pt)) { + // Find the orbit of pt and remove all other points of the orbit from bit_array + + set_bit_array(ORB_LOOKUP, pt, true); + ORB[0] = pt; + uint16_t n = 1; // lenght of the orbit of pt + + for (uint16_t i = 0; i < n; ++i) { + for (uint16_t j = 0; j < STAB_GENS[rep_depth]->size; ++j) { + Perm gen = STAB_GENS[rep_depth]->perms[j]; + uint16_t const img = gen[ORB[i]]; + if (!get_bit_array(ORB_LOOKUP, img)) { + ORB[n++] = img; + set_bit_array(ORB_LOOKUP, img, true); + set_bit_array(bit_array, img, false); + } + } + } + } + pt++; + } +} + + static void BronKerbosch(uint16_t depth, + uint16_t rep_depth, uint16_t limit, uint16_t* nr_found, bool max, @@ -129,23 +174,54 @@ static void BronKerbosch(uint16_t depth, copy_bit_array(to_try, try, nr); } - for (uint16_t i = 0; i < nr; i++) { - if (get_bit_array(to_try, i)){ - set_bit_array(CLIQUE, i, true); + // Get orbit representatives of + get_orbit_reps_bitarray(to_try, rep_depth); - push_conditions(TRY, depth + 1, 0, GRAPH->neighbours[i]); - push_conditions(BAN, depth + 1, 0, GRAPH->neighbours[i]); + for (uint16_t pt = 0; pt < nr; pt++) { + if (get_bit_array(to_try, pt)){ + set_bit_array(CLIQUE, pt, true); + + push_conditions(TRY, depth + 1, 0, GRAPH->neighbours[pt]); + push_conditions(BAN, depth + 1, 0, GRAPH->neighbours[pt]); // recurse - BronKerbosch(depth + 1, limit, nr_found, max, size); + if (STAB_GENS[rep_depth]->size == 0) { + BronKerbosch(depth + 1, rep_depth, limit, nr_found, max, size); + } else { + // the point_stabilizer is very SLOW! + point_stabilizer(SCHREIER_SIMS, STAB_GENS[rep_depth], + STAB_GENS[rep_depth + 1], pt); + BronKerbosch(depth + 1, rep_depth + 1, limit, nr_found, max, size); + } pop_conditions(TRY, depth + 1); pop_conditions(BAN, depth + 1); - set_bit_array(CLIQUE, i, false); - - set_bit_array(get_conditions(TRY, 0), i , false); - set_bit_array(get_conditions(BAN, 0), i , true); + set_bit_array(CLIQUE, pt, false); + + // if (STAB_GENS[rep_depth]->size == 0) { + set_bit_array(get_conditions(TRY, 0), pt, false); + set_bit_array(get_conditions(BAN, 0), pt, true); + // } else { + // init_bit_array(ORB_LOOKUP, false, nr); + // set_bit_array(ORB_LOOKUP, pt, true); + // ORB[0] = pt; + // uint16_t n = 1; // lenght of the orbit of pt + + // for (uint16_t i = 0; i < n; ++i) { + // for (uint16_t j = 0; j < STAB_GENS[rep_depth]->size; ++j) { + // Perm gen = STAB_GENS[rep_depth]->perms[j]; + // uint16_t const img = gen[ORB[i]]; + // if (!get_bit_array(ORB_LOOKUP, img)) { + // ORB[n++] = img; + // set_bit_array(ORB_LOOKUP, img, true); + // } + // } + // } + // complement_bit_arrays(get_conditions(TRY,0), ORB_LOOKUP, nr); + // union_bit_arrays(get_conditions(BAN,0), ORB_LOOKUP, nr); + // } } + } } @@ -183,7 +259,6 @@ static Obj clique_hook_gap(void* user_param, const BitArray* clique, const uint1 return CALL_2ARGS(GAP_FUNC, user_param, c); } - static void init_clique_graph_from_digraph_obj(Graph* const graph, Obj digraph_obj) { DIGRAPHS_ASSERT(graph != NULL); @@ -195,8 +270,6 @@ static void init_clique_graph_from_digraph_obj(Graph* const graph, DIGRAPHS_ASSERT(IS_PLIST(out)); clear_graph(graph, nr); - - // Only include symmetric edges for (uint16_t i = 1; i <= nr; i++) { Obj row = ELM_PLIST(adj_mat, i); @@ -215,7 +288,8 @@ static bool init_clique_data_from_args(Obj digraph_obj, Obj include_obj, Obj exclude_obj, Obj max_obj, - Obj size_obj) { + Obj size_obj, + Obj aut_grp_obj) { static bool is_initialized = false; if (!is_initialized) { is_initialized = true; @@ -227,6 +301,12 @@ static bool init_clique_data_from_args(Obj digraph_obj, CLIQUE = new_bit_array(MAXVERTS); TRY = new_conditions(MAXVERTS, MAXVERTS); BAN = new_conditions(MAXVERTS, MAXVERTS); + + ORB_LOOKUP = new_bit_array(MAXVERTS); + for (uint16_t i = 0; i < MAXVERTS; i++) { + STAB_GENS[i] = new_perm_coll(MAXVERTS, MAXVERTS); + } + SCHREIER_SIMS = new_schreier_sims(); } uint16_t nr = DigraphNrVertices(digraph_obj); @@ -245,13 +325,23 @@ static bool init_clique_data_from_args(Obj digraph_obj, } USER_PARAM = user_param_obj; + + // Get generators of the automorphism group the graph + PERM_DEGREE = nr; + if (aut_grp_obj == Fail) { + // TODO: should we use BLISS instead? Otherwise drop digraph directions in GAP + get_automorphism_group_from_gap(digraph_obj, STAB_GENS[0]); + } else { + set_automorphisms(aut_grp_obj, STAB_GENS[0]); + } + return true; } Obj FuncDigraphsCliqueFinder(Obj self, Obj args) { - if (LEN_PLIST(args) != 8) { + if (LEN_PLIST(args) != 8 && LEN_PLIST(args) != 9) { ErrorQuit( - "there must be 8 arguments, found %d,", LEN_PLIST(args), 0L); + "there must be 8 or 9 arguments, found %d,", LEN_PLIST(args), 0L); } Obj digraph_obj = ELM_PLIST(args, 1); Obj hook_obj = ELM_PLIST(args, 2); @@ -261,6 +351,11 @@ Obj FuncDigraphsCliqueFinder(Obj self, Obj args) { Obj exclude_obj = ELM_PLIST(args, 6); Obj max_obj = ELM_PLIST(args, 7); Obj size_obj = ELM_PLIST(args, 8); + Obj aut_grp_obj = Fail; + if (LEN_PLIST(args) == 9) { + aut_grp_obj = ELM_PLIST(args, 9); + } + // Validate the arguments if (CALL_1ARGS(IsDigraph, digraph_obj) != True) { @@ -312,6 +407,35 @@ Obj FuncDigraphsCliqueFinder(Obj self, Obj args) { 0L); } + if (aut_grp_obj != Fail) { + if (CALL_1ARGS(IsPermGroup, aut_grp_obj) != True) { + ErrorQuit( + "the 9th argument must be a permutation group " + "or fail, not %s,", + (Int) TNAM_OBJ(aut_grp_obj), + 0L); + } + Obj gens = CALL_1ARGS(GeneratorsOfGroup, aut_grp_obj); + DIGRAPHS_ASSERT(IS_LIST(gens)); + DIGRAPHS_ASSERT(LEN_LIST(gens) > 0); + UInt lmp = INT_INTOBJ(CALL_1ARGS(LargestMovedPointPerms, gens)); + if (lmp > 0 && LEN_LIST(gens) >= lmp) { + ErrorQuit("expected at most %d generators in the 9th argument " + "but got %d,", + lmp - 1, + LEN_LIST(gens)); + } + for (UInt i = 1; i <= LEN_LIST(gens); ++i) { + if (CALL_2ARGS(IsDigraphAutomorphism, digraph_obj, ELM_LIST(gens, i)) + != True) { + ErrorQuit("expected group of automorphisms, but found a " + "non-automorphism in position %d of the group generators,", + i, + 0L); + } + } + } + // Initialise all the variable which will be used to carry out the recursion if (!init_clique_data_from_args(digraph_obj, hook_obj, @@ -319,7 +443,8 @@ Obj FuncDigraphsCliqueFinder(Obj self, Obj args) { include_obj, exclude_obj, max_obj, - size_obj)) { + size_obj, + aut_grp_obj)) { return user_param_obj; } @@ -333,7 +458,7 @@ Obj FuncDigraphsCliqueFinder(Obj self, Obj args) { // go! if (setjmp(OUTOFHERE) == 0) { - BronKerbosch(0, limit, &nr_found, max, size); + BronKerbosch(0, 0, limit, &nr_found, max, size); } return user_param_obj; diff --git a/src/homos.c b/src/homos.c index e11024520..6aa3b199e 100644 --- a/src/homos.c +++ b/src/homos.c @@ -294,7 +294,7 @@ homo_hook_collect(void* user_param, uint16_t const nr, uint16_t const* map) { // printf(" }>"); // } -static void get_automorphism_group_from_gap(Obj digraph_obj, PermColl* out) { +void get_automorphism_group_from_gap(Obj digraph_obj, PermColl* out) { DIGRAPHS_ASSERT(CALL_1ARGS(IsDigraph, digraph_obj) == True); Obj o = CALL_1ARGS(AutomorphismGroup, digraph_obj); o = CALL_1ARGS(GeneratorsOfGroup, o); @@ -523,7 +523,7 @@ static void internal_order_map_graph(Graph const* const graph) { } } -static void set_automorphisms(Obj aut_grp_obj, PermColl* out) { +void set_automorphisms(Obj aut_grp_obj, PermColl* out) { DIGRAPHS_ASSERT(out != NULL); clear_perm_coll(out); out->degree = PERM_DEGREE; From 0d9135f6a36358dc57cc73eb2cef3a5d8c97954a Mon Sep 17 00:00:00 2001 From: Julius Date: Mon, 6 Apr 2020 18:17:31 +0200 Subject: [PATCH 19/30] add: now uses include parameter correctly --- src/cliques.c | 167 +++++++++++++++++++++++++++++++++++++------------ src/digraphs.c | 2 + 2 files changed, 130 insertions(+), 39 deletions(-) diff --git a/src/cliques.c b/src/cliques.c index 25746d909..9a4ef79db 100644 --- a/src/cliques.c +++ b/src/cliques.c @@ -52,6 +52,7 @@ extern Obj AutomorphismGroup; extern Obj IsPermGroup; extern Obj IsDigraphAutomorphism; extern Obj LargestMovedPointPerms; +extern Obj IsClique; //////////////////////////////////////////////////////////////////////////////// // Global variables @@ -149,7 +150,7 @@ static void BronKerbosch(uint16_t depth, BitArray* to_try = new_bit_array(MAXVERTS); if (max) { - // Choose a pivot with as a many neighbours in as possible + // Choose a pivot with as many neighbours in as possible uint16_t pivot = 0; int max_neighbours = -1; for (uint16_t i = 0; i < nr; i++){ @@ -198,28 +199,28 @@ static void BronKerbosch(uint16_t depth, pop_conditions(BAN, depth + 1); set_bit_array(CLIQUE, pt, false); - // if (STAB_GENS[rep_depth]->size == 0) { + if (STAB_GENS[rep_depth]->size == 0) { set_bit_array(get_conditions(TRY, 0), pt, false); set_bit_array(get_conditions(BAN, 0), pt, true); - // } else { - // init_bit_array(ORB_LOOKUP, false, nr); - // set_bit_array(ORB_LOOKUP, pt, true); - // ORB[0] = pt; - // uint16_t n = 1; // lenght of the orbit of pt - - // for (uint16_t i = 0; i < n; ++i) { - // for (uint16_t j = 0; j < STAB_GENS[rep_depth]->size; ++j) { - // Perm gen = STAB_GENS[rep_depth]->perms[j]; - // uint16_t const img = gen[ORB[i]]; - // if (!get_bit_array(ORB_LOOKUP, img)) { - // ORB[n++] = img; - // set_bit_array(ORB_LOOKUP, img, true); - // } - // } - // } - // complement_bit_arrays(get_conditions(TRY,0), ORB_LOOKUP, nr); - // union_bit_arrays(get_conditions(BAN,0), ORB_LOOKUP, nr); - // } + } else { + init_bit_array(ORB_LOOKUP, false, nr); + set_bit_array(ORB_LOOKUP, pt, true); + ORB[0] = pt; + uint16_t n = 1; // lenght of the orbit of pt + + for (uint16_t i = 0; i < n; ++i) { + for (uint16_t j = 0; j < STAB_GENS[rep_depth]->size; ++j) { + Perm gen = STAB_GENS[rep_depth]->perms[j]; + uint16_t const img = gen[ORB[i]]; + if (!get_bit_array(ORB_LOOKUP, img)) { + ORB[n++] = img; + set_bit_array(ORB_LOOKUP, img, true); + } + } + } + complement_bit_arrays(get_conditions(TRY,0), ORB_LOOKUP, nr); + union_bit_arrays(get_conditions(BAN,0), ORB_LOOKUP, nr); + } } } @@ -288,7 +289,6 @@ static bool init_clique_data_from_args(Obj digraph_obj, Obj include_obj, Obj exclude_obj, Obj max_obj, - Obj size_obj, Obj aut_grp_obj) { static bool is_initialized = false; if (!is_initialized) { @@ -309,13 +309,25 @@ static bool init_clique_data_from_args(Obj digraph_obj, SCHREIER_SIMS = new_schreier_sims(); } + init_clique_graph_from_digraph_obj(GRAPH, digraph_obj); + uint16_t nr = DigraphNrVertices(digraph_obj); - init_bit_array(CLIQUE, false, nr); clear_conditions(TRY, nr + 1, nr); clear_conditions(BAN, nr + 1, nr); init_bit_array(BAN->bit_array[0], false, nr); - init_clique_graph_from_digraph_obj(GRAPH, digraph_obj); + // Update and CLIQUE and TRY using include_obj + if (include_obj != Fail) { + init_bit_array(CLIQUE, false, nr); + set_bit_array_from_gap_list(CLIQUE, include_obj); + complement_bit_arrays(get_conditions(TRY, 0), CLIQUE, nr); + for (uint16_t i = 1; i <= LEN_LIST(include_obj); ++i) { + intersect_bit_arrays(get_conditions(TRY, 0), + GRAPH->neighbours[INT_INTOBJ(ELM_LIST(include_obj, i)) - 1], + nr); + } + } + if (hook_obj != Fail) { GAP_FUNC = hook_obj; @@ -356,7 +368,6 @@ Obj FuncDigraphsCliqueFinder(Obj self, Obj args) { aut_grp_obj = ELM_PLIST(args, 9); } - // Validate the arguments if (CALL_1ARGS(IsDigraph, digraph_obj) != True) { ErrorQuit("the 1st argument must be a digraph, not %s,", @@ -380,28 +391,80 @@ Obj FuncDigraphsCliqueFinder(Obj self, Obj args) { "the 2rd argument must be a function with 2 arguments,", 0L, 0L); } if (!IS_INTOBJ(limit_obj) && limit_obj != Infinity) { - ErrorQuit("the 4th argument must be an integer " + ErrorQuit("the 4th argument must be an integer " "or infinity, not %s,", (Int) TNAM_OBJ(limit_obj), 0L); } else if (IS_INTOBJ(limit_obj) && INT_INTOBJ(limit_obj) <= 0) { - ErrorQuit("the 4th argument must be a positive integer, " + ErrorQuit("the 4th argument must be a positive integer, " "not %d,", INT_INTOBJ(limit_obj), 0L); } + if (!IS_LIST(include_obj) && include_obj != Fail) { + ErrorQuit("the 5th argument must be a list or fail, not %s,", + (Int) TNAM_OBJ(include_obj), + 0L); + } else if (IS_LIST(include_obj)) { + for (Int i = 1; i <= LEN_LIST(include_obj); ++i) { + if (!ISB_LIST(include_obj, i)) { + ErrorQuit("the 5th argument must be a dense list,", 0L, 0L); + } else if (!IS_POS_INT(ELM_LIST(include_obj, i))) { + ErrorQuit("the 5th argument must only contain positive " + "integers, but found %s in position %d,", + (Int) TNAM_OBJ(ELM_LIST(include_obj, i)), + i); + } else if (INT_INTOBJ(ELM_LIST(include_obj, i)) + > DigraphNrVertices(digraph_obj)) { + ErrorQuit("in the 5th argument position %d is out of range, " + "must be in the range [1, %d],", + i, + DigraphNrVertices(digraph_obj)); + } else if (INT_INTOBJ( + POS_LIST(include_obj, ELM_LIST(include_obj, i), INTOBJ_INT(0))) + < i) { + ErrorQuit("in the 5th argument position %d is a duplicate,", i, 0L); + } + } + } + if (!IS_LIST(exclude_obj) && exclude_obj != Fail) { + ErrorQuit("the 6th argument must be a list or fail, not %s,", + (Int) TNAM_OBJ(exclude_obj), + 0L); + } else if (IS_LIST(exclude_obj)) { + for (Int i = 1; i <= LEN_LIST(exclude_obj); ++i) { + if (!ISB_LIST(exclude_obj, i)) { + ErrorQuit("the 6th argument must be a dense list,", 0L, 0L); + } else if (!IS_POS_INT(ELM_LIST(exclude_obj, i))) { + ErrorQuit("the 6th argument must only contain positive " + "integers, but found %s in position %d,", + (Int) TNAM_OBJ(ELM_LIST(exclude_obj, i)), + i); + } else if (INT_INTOBJ(ELM_LIST(exclude_obj, i)) + > DigraphNrVertices(digraph_obj)) { + ErrorQuit("in the 6th argument position %d is out of range, " + "must be in the range [1, %d],", + i, + DigraphNrVertices(digraph_obj)); + } else if (INT_INTOBJ( + POS_LIST(exclude_obj, ELM_LIST(exclude_obj, i), INTOBJ_INT(0))) + < i) { + ErrorQuit("in the 6th argument position %d is a duplicate,", i, 0L); + } + } + } if (max_obj != True && max_obj != False) { - ErrorQuit("the 7th argument must true or false, not %s,", + ErrorQuit("the 7th argument must true or false, not %s,", (Int) TNAM_OBJ(max_obj), 0L); } if (!IS_INTOBJ(size_obj) && size_obj != Fail) { - ErrorQuit("the 8th argument must be an integer " + ErrorQuit("the 8th argument must be an integer " "or fail, not %s,", (Int) TNAM_OBJ(size_obj), 0L); } else if (IS_INTOBJ(size_obj) && INT_INTOBJ(size_obj) <= 0) { - ErrorQuit("the 8th argument must be a positive integer, " + ErrorQuit("the 8th argument must be a positive integer, " "not %d,", INT_INTOBJ(size_obj), 0L); @@ -436,6 +499,40 @@ Obj FuncDigraphsCliqueFinder(Obj self, Obj args) { } } + uint16_t size = (size_obj == Fail ? 0 : INT_INTOBJ(size_obj)); + uint16_t include_size = (include_obj == Fail ? 0 : LEN_LIST(include_obj)); + uint16_t exclude_size = (exclude_obj == Fail ? 0 : LEN_LIST(exclude_obj)); + uint16_t nr = DigraphNrVertices(digraph_obj); + + // Check the trivial cases: + // The desired clique is too small + if (size != 0 && include_size > size) { + return user_param_obj; + } + // The desired clique is too big + if (size != 0 && size > nr - exclude_size) { + return user_param_obj; + } + // Check if include and exclude have empty intersection + if (include_size != 0 && exclude_size != 0) { + bool lookup[MAXVERTS] = {false}; + for (uint16_t i = 0; i < include_size; ++i) { + lookup[INT_INTOBJ(ELM_LIST(include_obj, i)) - 1] = true; + } + for (uint16_t i = 0; i < exclude_size; ++i) { + if (lookup[INT_INTOBJ(ELM_LIST(exclude_obj, i)) - 1]) { + return user_param_obj; + } + } + } + // Check if the set we are trying to extend is a clique + if (include_obj != Fail && CALL_2ARGS(IsClique, digraph_obj, include_obj) == False) { + return user_param_obj; + } + // + uint16_t nr_found = 0; + uint16_t limit = (limit_obj == Infinity ? MAXVERTS : INT_INTOBJ(limit_obj)); + bool max = (max_obj == True ? true : false); // Initialise all the variable which will be used to carry out the recursion if (!init_clique_data_from_args(digraph_obj, hook_obj, @@ -443,22 +540,14 @@ Obj FuncDigraphsCliqueFinder(Obj self, Obj args) { include_obj, exclude_obj, max_obj, - size_obj, aut_grp_obj)) { return user_param_obj; } - uint16_t nr_found = 0; - uint16_t limit = (limit_obj == Infinity ? MAXVERTS : INT_INTOBJ(limit_obj)); - uint16_t size = (size_obj == Fail ? 0 : INT_INTOBJ(size_obj)); - bool max = (max_obj == True ? true : false); - if (size > GRAPH->nr_vertices) { - return user_param_obj; - } // go! if (setjmp(OUTOFHERE) == 0) { - BronKerbosch(0, 0, limit, &nr_found, max, size); + BronKerbosch(0, 0, limit, &nr_found, max, (size == 0 ? size : size - include_size)); } return user_param_obj; diff --git a/src/digraphs.c b/src/digraphs.c index ac0394632..efb42ae05 100644 --- a/src/digraphs.c +++ b/src/digraphs.c @@ -55,6 +55,7 @@ Obj IsAttributeStoringRepObj; Obj IsPermGroup; Obj IsDigraphAutomorphism; Obj LargestMovedPointPerms; +Obj IsClique; #if !defined(GAP_KERNEL_MAJOR_VERSION) || GAP_KERNEL_MAJOR_VERSION < 3 // compatibility with GAP <= 4.9 @@ -2329,6 +2330,7 @@ static Int InitKernel(StructInitInfo* module) { ImportGVarFromLibrary("IsPermGroup", &IsPermGroup); ImportGVarFromLibrary("IsDigraphAutomorphism", &IsDigraphAutomorphism); ImportGVarFromLibrary("LargestMovedPointPerms", &LargestMovedPointPerms); + ImportGVarFromLibrary("IsClique", &IsClique); /* return success */ return 0; } From 59f33fdb95df5aa1ab2980f608d50f135fc15f39 Mon Sep 17 00:00:00 2001 From: Julius Date: Wed, 22 Apr 2020 14:39:32 +0200 Subject: [PATCH 20/30] add: first draft --- src/cliques.c | 415 ++++++++++++++++++++++++++++++------------------- src/cliques.h | 2 +- src/digraphs.c | 8 +- src/homos.c | 4 +- 4 files changed, 264 insertions(+), 165 deletions(-) diff --git a/src/cliques.c b/src/cliques.c index 9a4ef79db..0140ba117 100644 --- a/src/cliques.c +++ b/src/cliques.c @@ -8,13 +8,11 @@ // C headers -#include // for CHAR_BIT #include // for longjmp, setjmp, jmp_buf #include // for true, false, bool #include // for NULL #include // for uint16_t, uint64_t #include // for malloc, NULL -#include // for time // GAP headers #include "src/compiled.h" @@ -22,12 +20,16 @@ // Digraphs package headers #include "bitarray.h" // for BitArray #include "conditions.h" // for Conditions -#include "digraphs-config.h" // for DIGRAPHS_HAVE___BUILTIN_CTZLL #include "digraphs-debug.h" // for DIGRAPHS_ASSERT #include "homos-graphs.h" // for Digraph, Graph, . . . #include "perms.h" // for MAXVERTS, UNDEFINED, PermColl, Perm #include "schreier-sims.h" // for PermColl, . . . -#include "cliques.h" + +//////////////////////////////////////////////////////////////////////////////// +// Macros +//////////////////////////////////////////////////////////////////////////////// + +#define MIN(a, b) (a < b ? a : b) //////////////////////////////////////////////////////////////////////////////// // Forward declarations @@ -38,13 +40,8 @@ Int DigraphNrVertices(Obj); Obj FuncOutNeighbours(Obj, Obj); Obj FuncADJACENCY_MATRIX(Obj, Obj); -// Defined in homos.c -void set_automorphisms(Obj, PermColl*); -void get_automorphism_group_from_gap(Obj, PermColl*); - // GAP level things, imported in digraphs.c extern Obj IsDigraph; -extern Obj DIGRAPHS_ValidateVertexColouring; extern Obj Infinity; extern Obj IsSymmetricDigraph; extern Obj GeneratorsOfGroup; @@ -52,40 +49,80 @@ extern Obj AutomorphismGroup; extern Obj IsPermGroup; extern Obj IsDigraphAutomorphism; extern Obj LargestMovedPointPerms; +extern Obj SmallestMovedPointPerm; extern Obj IsClique; //////////////////////////////////////////////////////////////////////////////// // Global variables //////////////////////////////////////////////////////////////////////////////// -static Obj GAP_FUNC; // Variable to hold a GAP level hook function +static Obj GAP_FUNC; // Variable to hold a GAP level hook function static Obj (*HOOK)(void*, // HOOK function applied to every homo found const BitArray*, const uint16_t); -static void* USER_PARAM; // a USER_PARAM for the hook +static void* USER_PARAM; // A USER_PARAM for the hook -// Values in MAP are restricted to those positions in IMAGE_RESTRICT -static jmp_buf OUTOFHERE; // so we can jump out of the deepest +static jmp_buf OUTOFHERE; // So we can jump out of the deepest -static Graph* GRAPH; // Graphs to hold incoming GAP symmetric digraphs +static Graph* GRAPH; // Graphs to hold incoming GAP symmetric digraphs static BitArray* CLIQUE; static Conditions* TRY; static Conditions* BAN; -static uint16_t ORB[MAXVERTS]; // Array for containing nodes in an orbit. -static BitArray* ORB_LOOKUP; // points in orbit -static PermColl* STAB_GENS[MAXVERTS]; // stabiliser generators +static uint16_t ORB[MAXVERTS]; // Array for containing nodes in an orbit. +static BitArray* ORB_LOOKUP; // points in orbit +static PermColl* STAB_GENS[MAXVERTS]; // stabiliser generators static SchreierSims* SCHREIER_SIMS; //////////////////////////////////////////////////////////////////////////////// -// +// Hook functions //////////////////////////////////////////////////////////////////////////////// -// Update a BitArray to only include one vertex per orbit of a group generated -// by STAB_GENS[rep_depth] -static void get_orbit_reps_bitarray(BitArray* bit_array, uint16_t const rep_depth) { +static Obj clique_hook_collect(void* user_param, + const BitArray* clique, + const uint16_t nr) { + UInt i; + Obj c; + + if (TNUM_OBJ((Obj) user_param) == T_PLIST_EMPTY) { + RetypeBag(user_param, T_PLIST); + } + + c = NEW_PLIST(T_PLIST, nr); + for(i = 1; i <= nr; i++) { + if (get_bit_array(clique, i - 1)){ + PushPlist(c, INTOBJ_INT(i)); + } + } + + ASS_LIST(user_param, LEN_LIST(user_param) + 1, c); + return False; +} + +static Obj clique_hook_gap(void* user_param, const BitArray* clique, const uint16_t nr) { + UInt i; + Obj c; + + c = NEW_PLIST(T_PLIST, nr); + for(i = 1; i <= nr; i++) { + if (get_bit_array(clique, i-1)) { + PushPlist(c, INTOBJ_INT(i + 1)); + } + } + + return CALL_2ARGS(GAP_FUNC, user_param, c); +} + +//////////////////////////////////////////////////////////////////////////////// +// Static helper functions +//////////////////////////////////////////////////////////////////////////////// + +// Update a BitArray to only include only one vertex per orbit with respect to +// the group generated by STAB_GENS[rep_depth] +static void get_orbit_reps_bitarray(BitArray* bit_array, + uint16_t const rep_depth) { if (STAB_GENS[rep_depth]->size == 0) { return; } @@ -95,7 +132,8 @@ static void get_orbit_reps_bitarray(BitArray* bit_array, uint16_t const rep_dept init_bit_array(ORB_LOOKUP, false, nr); while (pt < nr) { if (get_bit_array(bit_array, pt)) { - // Find the orbit of pt and remove all other points of the orbit from bit_array + // Find the orbit of pt and remove all other points of the orbit from + // set_bit_array(ORB_LOOKUP, pt, true); ORB[0] = pt; @@ -117,13 +155,194 @@ static void get_orbit_reps_bitarray(BitArray* bit_array, uint16_t const rep_dept } } +// Get the automorphism group from gap, but discard the generators which act on +// isolated vertices, in particular, if the SmallestMovedPoint is in isolated +// TODO: Are we discarding too much? +static void get_automorphism_group_from_gap(Obj digraph_obj, + PermColl* out, + BitArray* isolated) { + DIGRAPHS_ASSERT(CALL_1ARGS(IsDigraph, digraph_obj) == True); + DIGRAPHS_ASSERT(out != NULL); + Obj gens = CALL_1ARGS(GeneratorsOfGroup, CALL_1ARGS(AutomorphismGroup, digraph_obj)); + DIGRAPHS_ASSERT(IS_LIST(gens)); + clear_perm_coll(out); + out->degree = PERM_DEGREE; + DIGRAPHS_ASSERT(out->capacity >= LEN_LIST(gens)); + Int nr_added = 1; + for (Int i = 1; i <= LEN_LIST(gens); ++i) { + DIGRAPHS_ASSERT(ISB_LIST(gens, i)); + DIGRAPHS_ASSERT(IS_PERM2(ELM_LIST(gens, i)) || IS_PERM4(ELM_LIST(gens, i))); + Obj gen = ELM_LIST(gens, i); + DIGRAPHS_ASSERT(LargestMovedPointPerm(gen) <= PERM_DEGREE); + // Check if the generator acts on the isolated points + if (LargestMovedPointPerm(gen) > 0 && !get_bit_array(isolated, + INT_INTOBJ(CALL_1ARGS(SmallestMovedPointPerm, gen)) - 1)) { + Perm perm = out->perms[nr_added - 1]; + out->size++; + DIGRAPHS_ASSERT(out->size == nr_added); + size_t dep; + if (IS_PERM2(gen)) { + UInt2 const* ptp2 = CONST_ADDR_PERM2(gen); + dep = MIN((uint16_t) DEG_PERM2(gen), PERM_DEGREE); + for (uint16_t j = 0; j < dep; ++j) { + perm[j] = (uint16_t) ptp2[j]; + } + } else { + DIGRAPHS_ASSERT(IS_PERM4(gen)); + UInt4 const* ptp4 = CONST_ADDR_PERM4(gen); + dep = MIN((uint16_t) DEG_PERM4(gen), PERM_DEGREE); + for (uint16_t j = 0; j < dep; ++j) { + perm[j] = (uint16_t) ptp4[j]; + } + } + for (uint16_t j = dep; j < PERM_DEGREE; ++j) { + perm[j] = j; + } + nr_added++; + } + } +} + +static void set_automorphisms(Obj aut_grp_obj, + PermColl* out, + BitArray* isolated) { + DIGRAPHS_ASSERT(out != NULL); + clear_perm_coll(out); + out->degree = PERM_DEGREE; + Obj gens = CALL_1ARGS(GeneratorsOfGroup, aut_grp_obj); + DIGRAPHS_ASSERT(IS_LIST(gens)); + DIGRAPHS_ASSERT(LEN_LIST(gens) > 0); + for (UInt i = 1; i <= LEN_LIST(gens); ++i) { + Obj gen_obj = ELM_LIST(gens, i); + if (LargestMovedPointPerm(gen_obj) > 0 && !get_bit_array(isolated, + INT_INTOBJ(CALL_1ARGS(SmallestMovedPointPerm, gen_obj)) - 1)) { + add_perm_coll(out, new_perm_from_gap(gen_obj, PERM_DEGREE)); + } + } +} + +static void init_graph_from_digraph_obj(Graph* const graph, + Obj digraph_obj) { + DIGRAPHS_ASSERT(graph != NULL); + DIGRAPHS_ASSERT(CALL_1ARGS(IsDigraph, digraph_obj) == True); + UInt const nr = DigraphNrVertices(digraph_obj); + Obj out = FuncOutNeighbours(0L, digraph_obj); + Obj adj_mat = FuncADJACENCY_MATRIX(0L, digraph_obj); + DIGRAPHS_ASSERT(nr < MAXVERTS); + DIGRAPHS_ASSERT(IS_PLIST(adj_mat)); + DIGRAPHS_ASSERT(IS_PLIST(out)); + clear_graph(graph, nr); + + // Only include symmetric edges + for (Int i = 1; i <= nr; i++) { + Obj row = ELM_PLIST(adj_mat, i); + DIGRAPHS_ASSERT(IS_LIST(row)); + Obj zero_obj = INTOBJ_INT(0); + for (Int j = 1; j <= nr; j++) { + if (ELM_PLIST(row, j) != zero_obj && ELM_PLIST(ELM_PLIST(adj_mat,j) ,i) != zero_obj){ + add_edge_graph(graph, i - 1, j - 1); + } + } + } +} + +static bool init_data_from_args(Obj digraph_obj, + Obj hook_obj, + Obj user_param_obj, + Obj include_obj, + Obj exclude_obj, + Obj max_obj, + Obj aut_grp_obj) { + static bool is_initialized = false; + if (!is_initialized) { + is_initialized = true; + + GRAPH = new_graph(MAXVERTS); + + // Currently Conditions are a nr1 x nr1 array of BitArrays, so both + // values have to be set to MAXVERTS + CLIQUE = new_bit_array(MAXVERTS); + TRY = new_conditions(MAXVERTS, MAXVERTS); + BAN = new_conditions(MAXVERTS, MAXVERTS); + + ORB_LOOKUP = new_bit_array(MAXVERTS); + for (uint16_t i = 0; i < MAXVERTS; i++) { + STAB_GENS[i] = new_perm_coll(MAXVERTS, MAXVERTS); + } + SCHREIER_SIMS = new_schreier_sims(); + } + + uint16_t nr = DigraphNrVertices(digraph_obj); + init_graph_from_digraph_obj(GRAPH, digraph_obj); + // Get the isolated vertices of the graph + BitArray* isolated = new_bit_array(MAXVERTS); + Int first_isolated = -1; + for (uint16_t i = 0; i < nr; ++i) { + if (size_bit_array(GRAPH->neighbours[i], nr) == 0) { + if (first_isolated == -1) { + first_isolated = i; + } + set_bit_array(isolated, i, true); + } + } + + clear_conditions(TRY, nr + 1, nr); + clear_conditions(BAN, nr + 1, nr); + init_bit_array(BAN->bit_array[0], false, nr); + + init_bit_array(CLIQUE, false, nr); + // Update CLIQUE and TRY using include_obj + if (include_obj != Fail) { + set_bit_array_from_gap_list(CLIQUE, include_obj); + complement_bit_arrays(get_conditions(TRY, 0), CLIQUE, nr); + for (uint16_t i = 1; i <= LEN_LIST(include_obj); ++i) { + intersect_bit_arrays(get_conditions(TRY, 0), + GRAPH->neighbours[INT_INTOBJ(ELM_LIST(include_obj, i)) - 1], + nr); + } + } + // Update TRY using exclude_obj + if (exclude_obj != Fail) { + BitArray* exclude = new_bit_array(MAXVERTS); + set_bit_array_from_gap_list(exclude, exclude_obj); + complement_bit_arrays(get_conditions(TRY, 0), exclude, nr); + } + // Update TRY using isolated + if (first_isolated != -1) { + complement_bit_arrays(get_conditions(TRY, 0), isolated, nr); + set_bit_array(get_conditions(TRY, 0), first_isolated, true); + } + + if (hook_obj != Fail) { + GAP_FUNC = hook_obj; + HOOK = clique_hook_gap; + } else { + HOOK = clique_hook_collect; + } + USER_PARAM = user_param_obj; + + // Get generators of the automorphism group the graph + // TODO: Shouldn't we also compute the stabilizer of the group on included setwise? + PERM_DEGREE = nr; + if (aut_grp_obj == Fail) { + get_automorphism_group_from_gap(digraph_obj, STAB_GENS[0], isolated); + } else { + set_automorphisms(aut_grp_obj, STAB_GENS[0], isolated); + } + + return true; +} + +//////////////////////////////////////////////////////////////////////////////// +// Main functions +//////////////////////////////////////////////////////////////////////////////// static void BronKerbosch(uint16_t depth, uint16_t rep_depth, - uint16_t limit, - uint16_t* nr_found, + uint64_t limit, + uint64_t* nr_found, bool max, - uint16_t size) { + uint16_t size) { uint16_t nr = GRAPH->nr_vertices; BitArray* try = get_conditions(TRY, 0); @@ -222,135 +441,10 @@ static void BronKerbosch(uint16_t depth, union_bit_arrays(get_conditions(BAN,0), ORB_LOOKUP, nr); } } - - } -} - - -static Obj clique_hook_collect(void* user_param, const BitArray* clique, const uint16_t nr) { - UInt i; - Obj c; - - if (TNUM_OBJ((Obj) user_param) == T_PLIST_EMPTY) { - RetypeBag(user_param, T_PLIST); - } - - c = NEW_PLIST(T_PLIST, nr); - for(i = 1; i <= nr; i++) { - if (get_bit_array(clique, i - 1)){ - PushPlist(c, ObjInt_UInt(i)); - } - } - - ASS_LIST(user_param, LEN_LIST(user_param) + 1, c); - return False; -} - -static Obj clique_hook_gap(void* user_param, const BitArray* clique, const uint16_t nr) { - UInt i; - Obj c; - - c = NEW_PLIST(T_PLIST, nr); - for(i = 1; i <= nr; i++) { - if (get_bit_array(clique, i-1)) { - PushPlist(c, ObjInt_UInt(i + 1)); - } - } - - return CALL_2ARGS(GAP_FUNC, user_param, c); -} - -static void init_clique_graph_from_digraph_obj(Graph* const graph, - Obj digraph_obj) { - DIGRAPHS_ASSERT(graph != NULL); - DIGRAPHS_ASSERT(CALL_1ARGS(IsDigraph, digraph_obj) == True); - UInt const nr = DigraphNrVertices(digraph_obj); - Obj out = FuncOutNeighbours(0L, digraph_obj); - Obj adj_mat = FuncADJACENCY_MATRIX(0L, digraph_obj); - DIGRAPHS_ASSERT(nr < MAXVERTS); - DIGRAPHS_ASSERT(IS_PLIST(out)); - clear_graph(graph, nr); - - // Only include symmetric edges - for (uint16_t i = 1; i <= nr; i++) { - Obj row = ELM_PLIST(adj_mat, i); - DIGRAPHS_ASSERT(IS_LIST(row)); - for (uint16_t j = 1; j <= nr; j++) { - if (ELM_PLIST(row, j) != INTOBJ_INT(0) && ELM_PLIST(ELM_PLIST(adj_mat,j) ,i) != INTOBJ_INT(0)){ - add_edge_graph(graph, i - 1, j - 1); - } - } } } -static bool init_clique_data_from_args(Obj digraph_obj, - Obj hook_obj, - Obj user_param_obj, - Obj include_obj, - Obj exclude_obj, - Obj max_obj, - Obj aut_grp_obj) { - static bool is_initialized = false; - if (!is_initialized) { - is_initialized = true; - - GRAPH = new_graph(MAXVERTS); - - // Currently Conditions are a nr1 x nr1 array of BitArrays, so both - // values have to be set to MAXVERTS - CLIQUE = new_bit_array(MAXVERTS); - TRY = new_conditions(MAXVERTS, MAXVERTS); - BAN = new_conditions(MAXVERTS, MAXVERTS); - - ORB_LOOKUP = new_bit_array(MAXVERTS); - for (uint16_t i = 0; i < MAXVERTS; i++) { - STAB_GENS[i] = new_perm_coll(MAXVERTS, MAXVERTS); - } - SCHREIER_SIMS = new_schreier_sims(); - } - - init_clique_graph_from_digraph_obj(GRAPH, digraph_obj); - - uint16_t nr = DigraphNrVertices(digraph_obj); - clear_conditions(TRY, nr + 1, nr); - clear_conditions(BAN, nr + 1, nr); - init_bit_array(BAN->bit_array[0], false, nr); - - // Update and CLIQUE and TRY using include_obj - if (include_obj != Fail) { - init_bit_array(CLIQUE, false, nr); - set_bit_array_from_gap_list(CLIQUE, include_obj); - complement_bit_arrays(get_conditions(TRY, 0), CLIQUE, nr); - for (uint16_t i = 1; i <= LEN_LIST(include_obj); ++i) { - intersect_bit_arrays(get_conditions(TRY, 0), - GRAPH->neighbours[INT_INTOBJ(ELM_LIST(include_obj, i)) - 1], - nr); - } - } - - - if (hook_obj != Fail) { - GAP_FUNC = hook_obj; - HOOK = clique_hook_gap; - } else { - HOOK = clique_hook_collect; - } - USER_PARAM = user_param_obj; - - - // Get generators of the automorphism group the graph - PERM_DEGREE = nr; - if (aut_grp_obj == Fail) { - // TODO: should we use BLISS instead? Otherwise drop digraph directions in GAP - get_automorphism_group_from_gap(digraph_obj, STAB_GENS[0]); - } else { - set_automorphisms(aut_grp_obj, STAB_GENS[0]); - } - - return true; -} - -Obj FuncDigraphsCliqueFinder(Obj self, Obj args) { +Obj FuncDigraphsCliquesFinder(Obj self, Obj args) { if (LEN_PLIST(args) != 8 && LEN_PLIST(args) != 9) { ErrorQuit( "there must be 8 or 9 arguments, found %d,", LEN_PLIST(args), 0L); @@ -473,7 +567,7 @@ Obj FuncDigraphsCliqueFinder(Obj self, Obj args) { if (aut_grp_obj != Fail) { if (CALL_1ARGS(IsPermGroup, aut_grp_obj) != True) { ErrorQuit( - "the 9th argument must be a permutation group " + "the 10th argument must be a permutation group " "or fail, not %s,", (Int) TNAM_OBJ(aut_grp_obj), 0L); @@ -483,7 +577,7 @@ Obj FuncDigraphsCliqueFinder(Obj self, Obj args) { DIGRAPHS_ASSERT(LEN_LIST(gens) > 0); UInt lmp = INT_INTOBJ(CALL_1ARGS(LargestMovedPointPerms, gens)); if (lmp > 0 && LEN_LIST(gens) >= lmp) { - ErrorQuit("expected at most %d generators in the 9th argument " + ErrorQuit("expected at most %d generators in the 10th argument " "but got %d,", lmp - 1, LEN_LIST(gens)); @@ -498,7 +592,7 @@ Obj FuncDigraphsCliqueFinder(Obj self, Obj args) { } } } - + uint16_t size = (size_obj == Fail ? 0 : INT_INTOBJ(size_obj)); uint16_t include_size = (include_obj == Fail ? 0 : LEN_LIST(include_obj)); uint16_t exclude_size = (exclude_obj == Fail ? 0 : LEN_LIST(exclude_obj)); @@ -529,12 +623,11 @@ Obj FuncDigraphsCliqueFinder(Obj self, Obj args) { if (include_obj != Fail && CALL_2ARGS(IsClique, digraph_obj, include_obj) == False) { return user_param_obj; } - // - uint16_t nr_found = 0; - uint16_t limit = (limit_obj == Infinity ? MAXVERTS : INT_INTOBJ(limit_obj)); + uint64_t nr_found = 0; + uint64_t limit = (limit_obj == Infinity ? SMALLINTLIMIT : INT_INTOBJ(limit_obj)); bool max = (max_obj == True ? true : false); // Initialise all the variable which will be used to carry out the recursion - if (!init_clique_data_from_args(digraph_obj, + if (!init_data_from_args(digraph_obj, hook_obj, user_param_obj, include_obj, @@ -543,7 +636,11 @@ Obj FuncDigraphsCliqueFinder(Obj self, Obj args) { aut_grp_obj)) { return user_param_obj; } - + // The clique we are trying to extend is already big enough + if (size != 0 && include_size == size) { + HOOK(USER_PARAM, CLIQUE, nr); + return user_param_obj; + } // go! if (setjmp(OUTOFHERE) == 0) { diff --git a/src/cliques.h b/src/cliques.h index c8aa5581d..d953121b2 100644 --- a/src/cliques.h +++ b/src/cliques.h @@ -14,6 +14,6 @@ // GAP headers #include "src/compiled.h" -Obj FuncDigraphsCliqueFinder(Obj self, Obj args); +Obj FuncDigraphsCliquesFinder(Obj self, Obj args); #endif // DIGRAPHS_SRC_CLIQUES_H_ diff --git a/src/digraphs.c b/src/digraphs.c index efb42ae05..db85f7368 100644 --- a/src/digraphs.c +++ b/src/digraphs.c @@ -55,6 +55,7 @@ Obj IsAttributeStoringRepObj; Obj IsPermGroup; Obj IsDigraphAutomorphism; Obj LargestMovedPointPerms; +Obj SmallestMovedPointPerm; Obj IsClique; #if !defined(GAP_KERNEL_MAJOR_VERSION) || GAP_KERNEL_MAJOR_VERSION < 3 @@ -2252,12 +2253,12 @@ static StructGVarFunc GVarFuncs[] = { FuncHomomorphismDigraphsFinder, "src/homos.c:FuncHomomorphismDigraphsFinder"}, - {"DigraphsCliqueFinder", + {"DigraphsCliquesFinder", -1, "digraph, hook, user_param, limit, include, " "exclude, max, size", - FuncDigraphsCliqueFinder, - "src/cliques.c:FuncDigraphsCliqueFinder"}, + FuncDigraphsCliquesFinder, + "src/cliques.c:FuncDigraphsCliquesFinder"}, {"IS_PLANAR", 1, "digraph", FuncIS_PLANAR, "src/planar.c:FuncIS_PLANAR"}, @@ -2330,6 +2331,7 @@ static Int InitKernel(StructInitInfo* module) { ImportGVarFromLibrary("IsPermGroup", &IsPermGroup); ImportGVarFromLibrary("IsDigraphAutomorphism", &IsDigraphAutomorphism); ImportGVarFromLibrary("LargestMovedPointPerms", &LargestMovedPointPerms); + ImportGVarFromLibrary("SmallestMovedPointPerm", &SmallestMovedPointPerm); ImportGVarFromLibrary("IsClique", &IsClique); /* return success */ return 0; diff --git a/src/homos.c b/src/homos.c index 6aa3b199e..e11024520 100644 --- a/src/homos.c +++ b/src/homos.c @@ -294,7 +294,7 @@ homo_hook_collect(void* user_param, uint16_t const nr, uint16_t const* map) { // printf(" }>"); // } -void get_automorphism_group_from_gap(Obj digraph_obj, PermColl* out) { +static void get_automorphism_group_from_gap(Obj digraph_obj, PermColl* out) { DIGRAPHS_ASSERT(CALL_1ARGS(IsDigraph, digraph_obj) == True); Obj o = CALL_1ARGS(AutomorphismGroup, digraph_obj); o = CALL_1ARGS(GeneratorsOfGroup, o); @@ -523,7 +523,7 @@ static void internal_order_map_graph(Graph const* const graph) { } } -void set_automorphisms(Obj aut_grp_obj, PermColl* out) { +static void set_automorphisms(Obj aut_grp_obj, PermColl* out) { DIGRAPHS_ASSERT(out != NULL); clear_perm_coll(out); out->degree = PERM_DEGREE; From 12de3df8e66ac77011697db3ce2d51dbdea9d03c Mon Sep 17 00:00:00 2001 From: Julius Date: Thu, 23 Apr 2020 10:56:31 +0200 Subject: [PATCH 21/30] doc: added descriptions of the arguments of the main function --- src/cliques.c | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/src/cliques.c b/src/cliques.c index 0140ba117..26c7704b7 100644 --- a/src/cliques.c +++ b/src/cliques.c @@ -444,6 +444,34 @@ static void BronKerbosch(uint16_t depth, } } +// FuncDigraphsCliquesFinder is the main function to use the C implementation +// of Bron-Kerbosch algorithm +// +// The arguments are as follows: +// +// 1. digraphs_obj the digraph to search for cliques in +// 2. hook_obj a funtion to apply to every clique found, or Fail if no +// such function is needed +// 3. user_param_obj GAP variable that can be used in the hook_obj, must be a +// plist if hook_obj is Fail +// 4. limit_obj the maximum number of cliques to find +// 5. include_obj a list of vertices of digraph_obj to required to be present in +// every clique found +// 6. exclude_obj a list of vertices of digraph_obj which cannot be present +// in any of the cliques found +// 7. max_obj True if only maximal cliques need to be found and False +// otherwise +// 8. size_obj an integer specifying the size of cliques to be found +// 9. aut_grp_obj an optional argument that can specifiy the automorphisms +// of the graph that will be used in the recursive search. +// If not given, the full automorphism group will be used. +// +// The function returns orbit representatives of cliques rather than all of the +// cliques themselves. Note that if include_obj or exlcude_obj are not invariant +// under the full automorphism group (or aut_grp_obj), then some of the cliques +// in the cliques in the orbit might not respect the inclusion/exclusion of +// the corresponding lists. + Obj FuncDigraphsCliquesFinder(Obj self, Obj args) { if (LEN_PLIST(args) != 8 && LEN_PLIST(args) != 9) { ErrorQuit( From 2fd6fa174968ef75806ef4220f3fa13290b39f84 Mon Sep 17 00:00:00 2001 From: Julius Date: Wed, 29 Apr 2020 15:06:39 +0200 Subject: [PATCH 22/30] add: fixed small bug and now discard gens which act on the isolated vertices. --- src/cliques.c | 321 +++++++++++++++++++++---------------------------- src/digraphs.c | 14 +++ 2 files changed, 149 insertions(+), 186 deletions(-) diff --git a/src/cliques.c b/src/cliques.c index 26c7704b7..a3bb0eba6 100644 --- a/src/cliques.c +++ b/src/cliques.c @@ -23,7 +23,6 @@ #include "digraphs-debug.h" // for DIGRAPHS_ASSERT #include "homos-graphs.h" // for Digraph, Graph, . . . #include "perms.h" // for MAXVERTS, UNDEFINED, PermColl, Perm -#include "schreier-sims.h" // for PermColl, . . . //////////////////////////////////////////////////////////////////////////////// // Macros @@ -51,6 +50,13 @@ extern Obj IsDigraphAutomorphism; extern Obj LargestMovedPointPerms; extern Obj SmallestMovedPointPerm; extern Obj IsClique; +extern Obj IsTrivial; +extern Obj Orbit; +extern Obj Stabilizer; +extern Obj IsSubset; +extern Obj OnTuples; +extern Obj Group; +extern Obj ClosureGroup; //////////////////////////////////////////////////////////////////////////////// // Global variables @@ -71,10 +77,8 @@ static BitArray* CLIQUE; static Conditions* TRY; static Conditions* BAN; -static uint16_t ORB[MAXVERTS]; // Array for containing nodes in an orbit. static BitArray* ORB_LOOKUP; // points in orbit -static PermColl* STAB_GENS[MAXVERTS]; // stabiliser generators -static SchreierSims* SCHREIER_SIMS; +static Obj ORBIT; //////////////////////////////////////////////////////////////////////////////// // Hook functions @@ -119,108 +123,32 @@ static Obj clique_hook_gap(void* user_param, const BitArray* clique, const uint1 // Static helper functions //////////////////////////////////////////////////////////////////////////////// -// Update a BitArray to only include only one vertex per orbit with respect to -// the group generated by STAB_GENS[rep_depth] -static void get_orbit_reps_bitarray(BitArray* bit_array, - uint16_t const rep_depth) { - if (STAB_GENS[rep_depth]->size == 0) { +// Update a BitArray to only include one vertex per orbit with respect to +// the group +static void get_orbit_reps_bitarray(BitArray* bit_array, Obj const group) { + if (group == Fail) { return; } uint16_t nr = GRAPH->nr_vertices; - uint16_t pt = 0; - init_bit_array(ORB_LOOKUP, false, nr); - while (pt < nr) { - if (get_bit_array(bit_array, pt)) { + for (uint16_t v = 0; v < nr; ++v) { + if (get_bit_array(bit_array, v)) { // Find the orbit of pt and remove all other points of the orbit from // - set_bit_array(ORB_LOOKUP, pt, true); - ORB[0] = pt; - uint16_t n = 1; // lenght of the orbit of pt - - for (uint16_t i = 0; i < n; ++i) { - for (uint16_t j = 0; j < STAB_GENS[rep_depth]->size; ++j) { - Perm gen = STAB_GENS[rep_depth]->perms[j]; - uint16_t const img = gen[ORB[i]]; - if (!get_bit_array(ORB_LOOKUP, img)) { - ORB[n++] = img; - set_bit_array(ORB_LOOKUP, img, true); - set_bit_array(bit_array, img, false); - } - } - } - } - pt++; - } -} + ORBIT = CALL_2ARGS(Orbit, group, INTOBJ_INT(v + 1)); + DIGRAPHS_ASSERT(IS_LIST(ORBIT)); -// Get the automorphism group from gap, but discard the generators which act on -// isolated vertices, in particular, if the SmallestMovedPoint is in isolated -// TODO: Are we discarding too much? -static void get_automorphism_group_from_gap(Obj digraph_obj, - PermColl* out, - BitArray* isolated) { - DIGRAPHS_ASSERT(CALL_1ARGS(IsDigraph, digraph_obj) == True); - DIGRAPHS_ASSERT(out != NULL); - Obj gens = CALL_1ARGS(GeneratorsOfGroup, CALL_1ARGS(AutomorphismGroup, digraph_obj)); - DIGRAPHS_ASSERT(IS_LIST(gens)); - clear_perm_coll(out); - out->degree = PERM_DEGREE; - DIGRAPHS_ASSERT(out->capacity >= LEN_LIST(gens)); - Int nr_added = 1; - for (Int i = 1; i <= LEN_LIST(gens); ++i) { - DIGRAPHS_ASSERT(ISB_LIST(gens, i)); - DIGRAPHS_ASSERT(IS_PERM2(ELM_LIST(gens, i)) || IS_PERM4(ELM_LIST(gens, i))); - Obj gen = ELM_LIST(gens, i); - DIGRAPHS_ASSERT(LargestMovedPointPerm(gen) <= PERM_DEGREE); - // Check if the generator acts on the isolated points - if (LargestMovedPointPerm(gen) > 0 && !get_bit_array(isolated, - INT_INTOBJ(CALL_1ARGS(SmallestMovedPointPerm, gen)) - 1)) { - Perm perm = out->perms[nr_added - 1]; - out->size++; - DIGRAPHS_ASSERT(out->size == nr_added); - size_t dep; - if (IS_PERM2(gen)) { - UInt2 const* ptp2 = CONST_ADDR_PERM2(gen); - dep = MIN((uint16_t) DEG_PERM2(gen), PERM_DEGREE); - for (uint16_t j = 0; j < dep; ++j) { - perm[j] = (uint16_t) ptp2[j]; - } - } else { - DIGRAPHS_ASSERT(IS_PERM4(gen)); - UInt4 const* ptp4 = CONST_ADDR_PERM4(gen); - dep = MIN((uint16_t) DEG_PERM4(gen), PERM_DEGREE); - for (uint16_t j = 0; j < dep; ++j) { - perm[j] = (uint16_t) ptp4[j]; - } - } - for (uint16_t j = dep; j < PERM_DEGREE; ++j) { - perm[j] = j; + for (Int i = 1; i <= LEN_LIST(ORBIT); ++i) { + set_bit_array(bit_array, INT_INTOBJ(ELM_LIST(ORBIT, i)) - 1, false); } - nr_added++; - } - } -} - -static void set_automorphisms(Obj aut_grp_obj, - PermColl* out, - BitArray* isolated) { - DIGRAPHS_ASSERT(out != NULL); - clear_perm_coll(out); - out->degree = PERM_DEGREE; - Obj gens = CALL_1ARGS(GeneratorsOfGroup, aut_grp_obj); - DIGRAPHS_ASSERT(IS_LIST(gens)); - DIGRAPHS_ASSERT(LEN_LIST(gens) > 0); - for (UInt i = 1; i <= LEN_LIST(gens); ++i) { - Obj gen_obj = ELM_LIST(gens, i); - if (LargestMovedPointPerm(gen_obj) > 0 && !get_bit_array(isolated, - INT_INTOBJ(CALL_1ARGS(SmallestMovedPointPerm, gen_obj)) - 1)) { - add_perm_coll(out, new_perm_from_gap(gen_obj, PERM_DEGREE)); + set_bit_array(bit_array, v, true); } } } +// Initialise the graph from GAP digraph and discard non-symmetric edges and +// loops static void init_graph_from_digraph_obj(Graph* const graph, Obj digraph_obj) { DIGRAPHS_ASSERT(graph != NULL); @@ -234,25 +162,27 @@ static void init_graph_from_digraph_obj(Graph* const graph, clear_graph(graph, nr); // Only include symmetric edges - for (Int i = 1; i <= nr; i++) { + for (Int i = 1; i < nr; i++) { Obj row = ELM_PLIST(adj_mat, i); DIGRAPHS_ASSERT(IS_LIST(row)); Obj zero_obj = INTOBJ_INT(0); - for (Int j = 1; j <= nr; j++) { + for (Int j = i + 1; j <= nr; j++) { if (ELM_PLIST(row, j) != zero_obj && ELM_PLIST(ELM_PLIST(adj_mat,j) ,i) != zero_obj){ add_edge_graph(graph, i - 1, j - 1); + add_edge_graph(graph, j - 1, i - 1); } } } } -static bool init_data_from_args(Obj digraph_obj, - Obj hook_obj, - Obj user_param_obj, - Obj include_obj, - Obj exclude_obj, - Obj max_obj, - Obj aut_grp_obj) { +// Initialise the global variables +static bool init_data_from_args(Obj digraph_obj, + Obj hook_obj, + Obj user_param_obj, + Obj include_obj, + Obj exclude_obj, + Obj max_obj, + Obj* group) { static bool is_initialized = false; if (!is_initialized) { is_initialized = true; @@ -265,26 +195,12 @@ static bool init_data_from_args(Obj digraph_obj, TRY = new_conditions(MAXVERTS, MAXVERTS); BAN = new_conditions(MAXVERTS, MAXVERTS); - ORB_LOOKUP = new_bit_array(MAXVERTS); - for (uint16_t i = 0; i < MAXVERTS; i++) { - STAB_GENS[i] = new_perm_coll(MAXVERTS, MAXVERTS); - } - SCHREIER_SIMS = new_schreier_sims(); + ORBIT = Fail; + ORB_LOOKUP = new_bit_array(MAXVERTS); } uint16_t nr = DigraphNrVertices(digraph_obj); init_graph_from_digraph_obj(GRAPH, digraph_obj); - // Get the isolated vertices of the graph - BitArray* isolated = new_bit_array(MAXVERTS); - Int first_isolated = -1; - for (uint16_t i = 0; i < nr; ++i) { - if (size_bit_array(GRAPH->neighbours[i], nr) == 0) { - if (first_isolated == -1) { - first_isolated = i; - } - set_bit_array(isolated, i, true); - } - } clear_conditions(TRY, nr + 1, nr); clear_conditions(BAN, nr + 1, nr); @@ -307,11 +223,41 @@ static bool init_data_from_args(Obj digraph_obj, set_bit_array_from_gap_list(exclude, exclude_obj); complement_bit_arrays(get_conditions(TRY, 0), exclude, nr); } - // Update TRY using isolated + + // Get the isolated vertices of the graph + BitArray* isolated = new_bit_array(MAXVERTS); + Int first_isolated = -1; + for (uint16_t i = 0; i < nr; ++i) { + if (size_bit_array(GRAPH->neighbours[i], nr) == 0) { + if (first_isolated == -1 && get_bit_array(get_conditions(TRY, 0), i)) { + first_isolated = i; + } + set_bit_array(isolated, i, true); + } + } + // Update TRY using isolated, only one isolated vertex is used if (first_isolated != -1) { complement_bit_arrays(get_conditions(TRY, 0), isolated, nr); set_bit_array(get_conditions(TRY, 0), first_isolated, true); } + + // Discard the generators of aut_grp_obj which act on the isolated vertices + if (size_bit_array(isolated, nr) > 0) { + Obj new_group = Fail; + Obj gens = CALL_1ARGS(GeneratorsOfGroup, *group); + DIGRAPHS_ASSERT(IS_LIST(gens)); + for (Int i = 1; i <= LEN_LIST(gens); ++i) { + Obj s = CALL_1ARGS(SmallestMovedPointPerm, ELM_LIST(gens, i)); + if (s != Infinity && !get_bit_array(isolated, INT_INTOBJ(s) - 1)) { + if (new_group == Fail) { + new_group = CALL_1ARGS(Group, ELM_LIST(gens, i)); + } else { + new_group = CALL_2ARGS(ClosureGroup, new_group, ELM_LIST(gens, i)); + } + } + } + *group = new_group; + } if (hook_obj != Fail) { GAP_FUNC = hook_obj; @@ -321,15 +267,6 @@ static bool init_data_from_args(Obj digraph_obj, } USER_PARAM = user_param_obj; - // Get generators of the automorphism group the graph - // TODO: Shouldn't we also compute the stabilizer of the group on included setwise? - PERM_DEGREE = nr; - if (aut_grp_obj == Fail) { - get_automorphism_group_from_gap(digraph_obj, STAB_GENS[0], isolated); - } else { - set_automorphisms(aut_grp_obj, STAB_GENS[0], isolated); - } - return true; } @@ -342,7 +279,8 @@ static void BronKerbosch(uint16_t depth, uint64_t limit, uint64_t* nr_found, bool max, - uint16_t size) { + uint16_t size, + Obj group) { uint16_t nr = GRAPH->nr_vertices; BitArray* try = get_conditions(TRY, 0); @@ -355,15 +293,14 @@ static void BronKerbosch(uint16_t depth, if (*nr_found >= limit) { longjmp(OUTOFHERE, 1); } - } else if (size_bit_array(try, nr) == 0 && size_bit_array(ban, nr) == 0) { + } else if (size_bit_array(try, nr) == 0 && size_bit_array(ban, nr) == 0 && + (size == 0 || size == depth)) { // is a maximal clique - if (size == 0 || size == depth) { - HOOK(USER_PARAM, CLIQUE, nr); - *nr_found += 1; - if (*nr_found >= limit) { - longjmp(OUTOFHERE, 1); - } - } + HOOK(USER_PARAM, CLIQUE, nr); + *nr_found += 1; + if (*nr_found >= limit) { + longjmp(OUTOFHERE, 1); + } return; } @@ -371,10 +308,11 @@ static void BronKerbosch(uint16_t depth, if (max) { // Choose a pivot with as many neighbours in as possible uint16_t pivot = 0; - int max_neighbours = -1; + int16_t max_neighbours = -1; + + BitArray* copy_try = new_bit_array(MAXVERTS); for (uint16_t i = 0; i < nr; i++){ if (get_bit_array(try, i) || get_bit_array(ban, i)){ - BitArray* copy_try = new_bit_array(MAXVERTS); // reuse it! copy_bit_array(copy_try, try, nr); intersect_bit_arrays(copy_try, GRAPH->neighbours[i], nr); uint16_t num_neighbours = size_bit_array(copy_try, nr); @@ -395,48 +333,36 @@ static void BronKerbosch(uint16_t depth, } // Get orbit representatives of - get_orbit_reps_bitarray(to_try, rep_depth); + get_orbit_reps_bitarray(to_try, group); - for (uint16_t pt = 0; pt < nr; pt++) { - if (get_bit_array(to_try, pt)){ - set_bit_array(CLIQUE, pt, true); + for (uint16_t v = 0; v < nr; v++) { + if (get_bit_array(to_try, v)){ + set_bit_array(CLIQUE, v, true); - push_conditions(TRY, depth + 1, 0, GRAPH->neighbours[pt]); - push_conditions(BAN, depth + 1, 0, GRAPH->neighbours[pt]); + push_conditions(TRY, depth + 1, 0, GRAPH->neighbours[v]); + push_conditions(BAN, depth + 1, 0, GRAPH->neighbours[v]); // recurse - if (STAB_GENS[rep_depth]->size == 0) { - BronKerbosch(depth + 1, rep_depth, limit, nr_found, max, size); + if (group == Fail) { + BronKerbosch(depth + 1, rep_depth, limit, nr_found, max, size, group); } else { - // the point_stabilizer is very SLOW! - point_stabilizer(SCHREIER_SIMS, STAB_GENS[rep_depth], - STAB_GENS[rep_depth + 1], pt); - BronKerbosch(depth + 1, rep_depth + 1, limit, nr_found, max, size); + Obj stabiliser = CALL_2ARGS(Stabilizer, group, INTOBJ_INT(v + 1)); + if (CALL_1ARGS(IsTrivial, stabiliser) == True) { + stabiliser = Fail; + } + BronKerbosch(depth + 1, rep_depth + 1, limit, nr_found, max, size, stabiliser); } pop_conditions(TRY, depth + 1); pop_conditions(BAN, depth + 1); - set_bit_array(CLIQUE, pt, false); + set_bit_array(CLIQUE, v, false); - if (STAB_GENS[rep_depth]->size == 0) { - set_bit_array(get_conditions(TRY, 0), pt, false); - set_bit_array(get_conditions(BAN, 0), pt, true); + if (group == Fail) { + set_bit_array(get_conditions(TRY, 0), v, false); + set_bit_array(get_conditions(BAN, 0), v, true); } else { - init_bit_array(ORB_LOOKUP, false, nr); - set_bit_array(ORB_LOOKUP, pt, true); - ORB[0] = pt; - uint16_t n = 1; // lenght of the orbit of pt - - for (uint16_t i = 0; i < n; ++i) { - for (uint16_t j = 0; j < STAB_GENS[rep_depth]->size; ++j) { - Perm gen = STAB_GENS[rep_depth]->perms[j]; - uint16_t const img = gen[ORB[i]]; - if (!get_bit_array(ORB_LOOKUP, img)) { - ORB[n++] = img; - set_bit_array(ORB_LOOKUP, img, true); - } - } - } + ORBIT = CALL_2ARGS(Orbit, group, INTOBJ_INT(v + 1)); + set_bit_array_from_gap_list(ORB_LOOKUP, ORBIT); complement_bit_arrays(get_conditions(TRY,0), ORB_LOOKUP, nr); union_bit_arrays(get_conditions(BAN,0), ORB_LOOKUP, nr); } @@ -456,9 +382,13 @@ static void BronKerbosch(uint16_t depth, // plist if hook_obj is Fail // 4. limit_obj the maximum number of cliques to find // 5. include_obj a list of vertices of digraph_obj to required to be present in -// every clique found +// every clique found. The list needs to be invariant under +// aut_grp_obj or the full automorphism group if aut_grp_obj +// is Fail // 6. exclude_obj a list of vertices of digraph_obj which cannot be present -// in any of the cliques found +// in any of the cliques found. The list needs to be +// invariant under aut_grp_obj or the full automorphism +// group if aut_grp_obj is Fail // 7. max_obj True if only maximal cliques need to be found and False // otherwise // 8. size_obj an integer specifying the size of cliques to be found @@ -466,11 +396,11 @@ static void BronKerbosch(uint16_t depth, // of the graph that will be used in the recursive search. // If not given, the full automorphism group will be used. // -// The function returns orbit representatives of cliques rather than all of the -// cliques themselves. Note that if include_obj or exlcude_obj are not invariant -// under the full automorphism group (or aut_grp_obj), then some of the cliques -// in the cliques in the orbit might not respect the inclusion/exclusion of -// the corresponding lists. +// Remarks: +// 1. The function returns orbit representatives of cliques rather than all of the +// cliques themselves. +// 2. Only one isolated vertex will be returned even if aut_grp_obj does not +// act transitevely on all isolated vertices. Obj FuncDigraphsCliquesFinder(Obj self, Obj args) { if (LEN_PLIST(args) != 8 && LEN_PLIST(args) != 9) { @@ -595,7 +525,7 @@ Obj FuncDigraphsCliquesFinder(Obj self, Obj args) { if (aut_grp_obj != Fail) { if (CALL_1ARGS(IsPermGroup, aut_grp_obj) != True) { ErrorQuit( - "the 10th argument must be a permutation group " + "the 9th argument must be a permutation group " "or fail, not %s,", (Int) TNAM_OBJ(aut_grp_obj), 0L); @@ -605,7 +535,7 @@ Obj FuncDigraphsCliquesFinder(Obj self, Obj args) { DIGRAPHS_ASSERT(LEN_LIST(gens) > 0); UInt lmp = INT_INTOBJ(CALL_1ARGS(LargestMovedPointPerms, gens)); if (lmp > 0 && LEN_LIST(gens) >= lmp) { - ErrorQuit("expected at most %d generators in the 10th argument " + ErrorQuit("expected at most %d generators in the 9th argument " "but got %d,", lmp - 1, LEN_LIST(gens)); @@ -619,6 +549,25 @@ Obj FuncDigraphsCliquesFinder(Obj self, Obj args) { 0L); } } + } else { + aut_grp_obj = CALL_1ARGS(AutomorphismGroup, digraph_obj); + } + + // Check that include_obj and exclude_obj are invariant under aut_grp_obj + Obj gens = CALL_1ARGS(GeneratorsOfGroup, aut_grp_obj); + DIGRAPHS_ASSERT(IS_LIST(gens)); + DIGRAPHS_ASSERT(LEN_LIST(gens) > 0); + for (UInt i = 1; i <= LEN_LIST(gens); ++i) { + if (include_obj != Fail && CALL_2ARGS(IsSubset, include_obj, + CALL_2ARGS(OnTuples, include_obj, ELM_LIST(gens, i))) != True) { + ErrorQuit("the 5th argument must be invaraint under , " + "or the full automorphism if is not given", 0L, 0L); + } + if (exclude_obj != Fail && CALL_2ARGS(IsSubset, exclude_obj, + CALL_2ARGS(OnTuples, exclude_obj, ELM_LIST(gens, i))) != True) { + ErrorQuit("the 6th argument must be invaraint under , " + "or the full automorphism if is not given", 0L, 0L); + } } uint16_t size = (size_obj == Fail ? 0 : INT_INTOBJ(size_obj)); @@ -656,12 +605,12 @@ Obj FuncDigraphsCliquesFinder(Obj self, Obj args) { bool max = (max_obj == True ? true : false); // Initialise all the variable which will be used to carry out the recursion if (!init_data_from_args(digraph_obj, - hook_obj, - user_param_obj, - include_obj, - exclude_obj, - max_obj, - aut_grp_obj)) { + hook_obj, + user_param_obj, + include_obj, + exclude_obj, + max_obj, + &aut_grp_obj)) { return user_param_obj; } // The clique we are trying to extend is already big enough @@ -672,7 +621,7 @@ Obj FuncDigraphsCliquesFinder(Obj self, Obj args) { // go! if (setjmp(OUTOFHERE) == 0) { - BronKerbosch(0, 0, limit, &nr_found, max, (size == 0 ? size : size - include_size)); + BronKerbosch(0, 0, limit, &nr_found, max, (size == 0 ? size : size - include_size), aut_grp_obj); } return user_param_obj; diff --git a/src/digraphs.c b/src/digraphs.c index db85f7368..66bf05af9 100644 --- a/src/digraphs.c +++ b/src/digraphs.c @@ -57,6 +57,13 @@ Obj IsDigraphAutomorphism; Obj LargestMovedPointPerms; Obj SmallestMovedPointPerm; Obj IsClique; +Obj IsTrivial; +Obj Orbit; +Obj Stabilizer; +Obj IsSubset; +Obj OnTuples; +Obj Group; +Obj ClosureGroup; #if !defined(GAP_KERNEL_MAJOR_VERSION) || GAP_KERNEL_MAJOR_VERSION < 3 // compatibility with GAP <= 4.9 @@ -2333,6 +2340,13 @@ static Int InitKernel(StructInitInfo* module) { ImportGVarFromLibrary("LargestMovedPointPerms", &LargestMovedPointPerms); ImportGVarFromLibrary("SmallestMovedPointPerm", &SmallestMovedPointPerm); ImportGVarFromLibrary("IsClique", &IsClique); + ImportGVarFromLibrary("IsTrivial", &IsTrivial); + ImportGVarFromLibrary("Orbit", &Orbit); + ImportGVarFromLibrary("Stabilizer", &Stabilizer); + ImportGVarFromLibrary("IsSubset", &IsSubset); + ImportGVarFromLibrary("OnTuples", &OnTuples); + ImportGVarFromLibrary("Group", &Group); + ImportGVarFromLibrary("ClosureGroup", &ClosureGroup); /* return success */ return 0; } From f1540292338a945b19a93edc5f41753f21f9f2e7 Mon Sep 17 00:00:00 2001 From: Julius Date: Mon, 4 May 2020 17:33:14 +0200 Subject: [PATCH 23/30] add: CliqueFinder uses C code when possible --- gap/cliques.gi | 164 +++++++++++++++++++++++++++++++++++++++++-------- src/cliques.c | 12 ++-- 2 files changed, 144 insertions(+), 32 deletions(-) diff --git a/gap/cliques.gi b/gap/cliques.gi index 42ae1b065..9399bbdb5 100644 --- a/gap/cliques.gi +++ b/gap/cliques.gi @@ -541,12 +541,16 @@ function(arg) return CliquesFinder(D, fail, [], limit, include, exclude, true, size, false); end); + +# A wrapper for DigraphsCliquesFinder +# This is very hacky at the moment, so we could test C code with GAP tests InstallGlobalFunction(CliquesFinder, -function(D, hook, user_param, limit, include, exclude, max, size, reps) - local n, sub, group, invariant_include, invariant_exclude, include_variant, - exclude_variant, x, v, o, i, out; +function(digraph, hook, user_param, limit, include, exclude, max, size, reps) + local n, subgraph, group, vertices, invariant_include, invariant_exclude, + include_variant, exclude_variant, include_invariant, exclude_invariant, + x, v, o, i, out, out_reps, found_orbits, num_found, add_cliques; - if not IsDigraph(D) then + if not IsDigraph(digraph) then ErrorNoReturn("the 1st argument must be a digraph,"); fi; @@ -565,7 +569,7 @@ function(D, hook, user_param, limit, include, exclude, max, size, reps) "a positive integer,"); fi; - n := DigraphNrVertices(D); + n := DigraphNrVertices(digraph); if not (IsHomogeneousList(include) and ForAll(include, x -> IsPosInt(x) and x <= n) and IsDuplicateFreeList(include)) @@ -591,21 +595,25 @@ function(D, hook, user_param, limit, include, exclude, max, size, reps) ErrorNoReturn("the 9th argument must be true or false,"); fi; - # Investigate whether and are invariant under - sub := DigraphMutableCopyIfMutable(D); - sub := MaximalSymmetricSubdigraphWithoutLoops(sub); - group := AutomorphismGroup(sub); + subgraph := DigraphMutableCopyIfMutable(digraph); + subgraph := MaximalSymmetricSubdigraphWithoutLoops(subgraph); + group := AutomorphismGroup(subgraph); + # Investigate whether and are invariant under + vertices := DigraphVertices(digraph); + include_variant := BlistList(vertices, []); + exclude_variant := BlistList(vertices, []); invariant_include := true; + include_invariant := include; invariant_exclude := true; - include_variant := []; - exclude_variant := []; + exclude_invariant := exclude; if not IsTrivial(group) and (not IsEmpty(include) or not IsEmpty(exclude)) then if not ForAll(GeneratorsOfGroup(group), x -> IsSubset(include, OnTuples(include, x))) then invariant_include := false; + include_invariant := []; if not reps then x := ShallowCopy(include); while not IsEmpty(x) do @@ -613,7 +621,9 @@ function(D, hook, user_param, limit, include, exclude, max, size, reps) o := List(Orbit(group, v)); i := Intersection(x, o); if not IsSubset(x, o) then - Append(include_variant, i); + UniteBlist(include_variant, BlistList(vertices, i)); + else + Append(include_invariant, i); fi; x := Difference(x, i); od; @@ -622,6 +632,7 @@ function(D, hook, user_param, limit, include, exclude, max, size, reps) if not ForAll(GeneratorsOfGroup(group), x -> IsSubset(exclude, OnTuples(exclude, x))) then invariant_exclude := false; + exclude_invariant := []; if not reps then x := ShallowCopy(exclude); while not IsEmpty(x) do @@ -629,7 +640,9 @@ function(D, hook, user_param, limit, include, exclude, max, size, reps) o := List(Orbit(group, v)); i := Intersection(x, o); if not IsSubset(x, o) then - Append(exclude_variant, i); + UniteBlist(exclude_variant, BlistList(vertices, i)); + else + Append(exclude_invariant, i); fi; x := Difference(x, i); od; @@ -641,21 +654,120 @@ function(D, hook, user_param, limit, include, exclude, max, size, reps) "5th arguments and must be ", "invariant under the action of the automorphism group of ", "the maximal symmetric subdigraph without loops,"); - fi; + fi; fi; - out := DIGRAPHS_BronKerbosch(D, - hook, - user_param, - limit, - include, - exclude, - max, - size, - reps, - include_variant, - exclude_variant); - return MakeImmutable(out); + if 0 < DigraphNrVertices(digraph) and DigraphNrVertices(digraph) < 512 then + if reps then + # Might want to pass the group here + out := DigraphsCliquesFinder(subgraph, + hook, + user_param, + limit, + include, + exclude, + max, + size); + return MakeImmutable(out); + else + out_reps := DigraphsCliquesFinder(subgraph, + fail, + [], + limit, + include_invariant, + exclude_invariant, + max, + size); + + # Function to find the valid cliques of an orbit given an orbit rep + found_orbits := []; + num_found := 0; + if hook = fail then + hook := Add; + fi; + + add_cliques := function(clique) + local orbit, n, i; + + if not ForAny(found_orbits, x -> clique in x) then + orbit := Orb(group, clique, OnSets); + Enumerate(orbit); + Add(found_orbits, orbit); + n := Length(orbit); + + if invariant_include and invariant_exclude then + # we're not just looking for orbit reps, but inc and exc are invariant + # so there is nothing extra to check + n := Minimum(limit - num_found, n); + for clique in orbit{[1 .. n]} do + hook(user_param, clique); + od; + num_found := num_found + n; + return; + fi; + + if invariant_include then + # Cliques in the orbit might contain forbidden vertices + i := 0; + while i < n and num_found < limit do + i := i + 1; + clique := BlistList(vertices, orbit[i]); + if SizeBlist(IntersectionBlist(exclude_variant, clique)) = 0 then + hook(user_param, orbit[i]); + num_found := num_found + 1; + fi; + od; + elif invariant_exclude then + # Cliques in the orbit might not contain all required vertices + i := 0; + while i < n and num_found < limit do + i := i + 1; + clique := BlistList(vertices, orbit[i]); + if IsSubsetBlist(clique, include_variant) then + hook(user_param, orbit[i]); + num_found := num_found + 1; + fi; + od; + else + # Cliques in the orbit might contain forbidden vertices and + # might not contain all required vertices + i := 0; + while i < n and num_found < limit do + i := i + 1; + clique := BlistList(vertices, orbit[i]); + if SizeBlist(IntersectionBlist(exclude_variant, clique)) = 0 + and IsSubsetBlist(clique, include_variant) then + hook(user_param, orbit[i]); + num_found := num_found + 1; + fi; + od; + fi; + fi; + return; + end; + + for x in out_reps do + add_cliques(x); + od; + return MakeImmutable(user_param); + fi; + else + include_variant := ListBlist(vertices, include_variant); + exclude_variant := ListBlist(vertices, exclude_variant); + + out := DIGRAPHS_BronKerbosch(digraph, + hook, + user_param, + limit, + include, + exclude, + max, + size, + reps, + include_variant, + exclude_variant); + return MakeImmutable(out); + fi; end); InstallGlobalFunction(DIGRAPHS_BronKerbosch, diff --git a/src/cliques.c b/src/cliques.c index a3bb0eba6..71b3ff377 100644 --- a/src/cliques.c +++ b/src/cliques.c @@ -1,12 +1,11 @@ //////////////////////////////////////////////////////////////////////////////// // -// cliques.c di/cliques Julius Jonusas +// cliques.c cliques Julius Jonusas // // Copyright (C) 2020 - Julius Jonusas // // This file is free software, see the digraphs/LICENSE. - // C headers #include // for longjmp, setjmp, jmp_buf #include // for true, false, bool @@ -303,7 +302,8 @@ static void BronKerbosch(uint16_t depth, } return; } - + + // TODO: BitArray* to_try = new_bit_array(MAXVERTS); if (max) { // Choose a pivot with as many neighbours in as possible @@ -426,10 +426,10 @@ Obj FuncDigraphsCliquesFinder(Obj self, Obj args) { (Int) TNAM_OBJ(digraph_obj), 0L); } else if (DigraphNrVertices(digraph_obj) > MAXVERTS) { - ErrorQuit("the 1st argument must have at most 512 vertices, " + ErrorQuit("the 1st argument must have at most %d vertices, " "found %d,", - DigraphNrVertices(digraph_obj), - 0L); + MAXVERTS, + DigraphNrVertices(digraph_obj)); } if (hook_obj == Fail) { if (!IS_LIST(user_param_obj) || !IS_MUTABLE_OBJ(user_param_obj)) { From ff2a26826dd19f4e48d6b81390600bfe10649929 Mon Sep 17 00:00:00 2001 From: Julius Date: Wed, 13 May 2020 10:54:09 +0200 Subject: [PATCH 24/30] add: implemented suggested technical changes --- gap/cliques.gi | 2 +- src/cliques.c | 238 +++++++++++++++++++++++++------------------------ 2 files changed, 123 insertions(+), 117 deletions(-) diff --git a/gap/cliques.gi b/gap/cliques.gi index 9399bbdb5..b76605a11 100644 --- a/gap/cliques.gi +++ b/gap/cliques.gi @@ -657,7 +657,7 @@ function(digraph, hook, user_param, limit, include, exclude, max, size, reps) fi; fi; - if 0 < DigraphNrVertices(digraph) and DigraphNrVertices(digraph) < 512 then + if DigraphNrVertices(digraph) < 512 then if reps then # Might want to pass the group here out := DigraphsCliquesFinder(subgraph, diff --git a/src/cliques.c b/src/cliques.c index 71b3ff377..234b763ca 100644 --- a/src/cliques.c +++ b/src/cliques.c @@ -7,7 +7,6 @@ // This file is free software, see the digraphs/LICENSE. // C headers -#include // for longjmp, setjmp, jmp_buf #include // for true, false, bool #include // for NULL #include // for uint16_t, uint64_t @@ -28,6 +27,7 @@ //////////////////////////////////////////////////////////////////////////////// #define MIN(a, b) (a < b ? a : b) +#define EXIT 0 //////////////////////////////////////////////////////////////////////////////// // Forward declarations @@ -61,23 +61,25 @@ extern Obj ClosureGroup; // Global variables //////////////////////////////////////////////////////////////////////////////// -static Obj GAP_FUNC; // Variable to hold a GAP level hook function - -static Obj (*HOOK)(void*, // HOOK function applied to every homo found - const BitArray*, - const uint16_t); -static void* USER_PARAM; // A USER_PARAM for the hook - -static jmp_buf OUTOFHERE; // So we can jump out of the deepest - -static Graph* GRAPH; // Graphs to hold incoming GAP symmetric digraphs - -static BitArray* CLIQUE; -static Conditions* TRY; -static Conditions* BAN; +struct clique_data { + void* user_param; // A USER_PARAM for the hook + Obj gap_func; // Variable to hold a GAP level hook function + Obj (*hook)(void*, // HOOK function applied to every homo found + const BitArray*, + const uint16_t, + Obj); + + Graph* graph; // Graphs to hold incoming GAP symmetric digraphs + BitArray* clique; + Conditions* try; + Conditions* ban; + Conditions* to_try; + + BitArray* temp_bitarray; + Obj orbit; +}; -static BitArray* ORB_LOOKUP; // points in orbit -static Obj ORBIT; +typedef struct clique_data CliqueData; //////////////////////////////////////////////////////////////////////////////// // Hook functions @@ -85,14 +87,11 @@ static Obj ORBIT; static Obj clique_hook_collect(void* user_param, const BitArray* clique, - const uint16_t nr) { + const uint16_t nr, + Obj gap_func) { UInt i; Obj c; - if (TNUM_OBJ((Obj) user_param) == T_PLIST_EMPTY) { - RetypeBag(user_param, T_PLIST); - } - c = NEW_PLIST(T_PLIST, nr); for(i = 1; i <= nr; i++) { if (get_bit_array(clique, i - 1)){ @@ -104,7 +103,7 @@ static Obj clique_hook_collect(void* user_param, return False; } -static Obj clique_hook_gap(void* user_param, const BitArray* clique, const uint16_t nr) { +static Obj clique_hook_gap(void* user_param, const BitArray* clique, const uint16_t nr, Obj gap_func) { UInt i; Obj c; @@ -115,7 +114,7 @@ static Obj clique_hook_gap(void* user_param, const BitArray* clique, const uint1 } } - return CALL_2ARGS(GAP_FUNC, user_param, c); + return CALL_2ARGS(gap_func, user_param, c); } //////////////////////////////////////////////////////////////////////////////// @@ -124,22 +123,24 @@ static Obj clique_hook_gap(void* user_param, const BitArray* clique, const uint1 // Update a BitArray to only include one vertex per orbit with respect to // the group -static void get_orbit_reps_bitarray(BitArray* bit_array, Obj const group) { +static void get_orbit_reps_bitarray(BitArray* bit_array, + Obj const group, + CliqueData* data) { if (group == Fail) { return; } - uint16_t nr = GRAPH->nr_vertices; + uint16_t nr = data->graph->nr_vertices; for (uint16_t v = 0; v < nr; ++v) { if (get_bit_array(bit_array, v)) { // Find the orbit of pt and remove all other points of the orbit from // - ORBIT = CALL_2ARGS(Orbit, group, INTOBJ_INT(v + 1)); - DIGRAPHS_ASSERT(IS_LIST(ORBIT)); + data->orbit = CALL_2ARGS(Orbit, group, INTOBJ_INT(v + 1)); + DIGRAPHS_ASSERT(IS_LIST(data->orbit)); - for (Int i = 1; i <= LEN_LIST(ORBIT); ++i) { - set_bit_array(bit_array, INT_INTOBJ(ELM_LIST(ORBIT, i)) - 1, false); + for (Int i = 1; i <= LEN_LIST(data->orbit); ++i) { + set_bit_array(bit_array, INT_INTOBJ(ELM_LIST(data->orbit, i)) - 1, false); } set_bit_array(bit_array, v, true); } @@ -181,38 +182,41 @@ static bool init_data_from_args(Obj digraph_obj, Obj include_obj, Obj exclude_obj, Obj max_obj, - Obj* group) { - static bool is_initialized = false; - if (!is_initialized) { - is_initialized = true; + Obj* group, + CliqueData* data) { + static bool is_initialised = false; + if (!is_initialised) { + is_initialised = true; - GRAPH = new_graph(MAXVERTS); + data->graph = new_graph(MAXVERTS); // Currently Conditions are a nr1 x nr1 array of BitArrays, so both // values have to be set to MAXVERTS - CLIQUE = new_bit_array(MAXVERTS); - TRY = new_conditions(MAXVERTS, MAXVERTS); - BAN = new_conditions(MAXVERTS, MAXVERTS); + data->clique = new_bit_array(MAXVERTS); + data->try = new_conditions(MAXVERTS, MAXVERTS); + data->ban = new_conditions(MAXVERTS, MAXVERTS); + data->to_try = new_conditions(MAXVERTS, MAXVERTS); - ORBIT = Fail; - ORB_LOOKUP = new_bit_array(MAXVERTS); + data->orbit = Fail; + data->temp_bitarray = new_bit_array(MAXVERTS); } uint16_t nr = DigraphNrVertices(digraph_obj); - init_graph_from_digraph_obj(GRAPH, digraph_obj); + init_graph_from_digraph_obj(data->graph, digraph_obj); - clear_conditions(TRY, nr + 1, nr); - clear_conditions(BAN, nr + 1, nr); - init_bit_array(BAN->bit_array[0], false, nr); + clear_conditions(data->try, nr + 1, nr); + clear_conditions(data->ban, nr + 1, nr); + clear_conditions(data->to_try, nr + 1, nr); + init_bit_array(data->ban->bit_array[0], false, nr); - init_bit_array(CLIQUE, false, nr); + init_bit_array(data->clique, false, nr); // Update CLIQUE and TRY using include_obj if (include_obj != Fail) { - set_bit_array_from_gap_list(CLIQUE, include_obj); - complement_bit_arrays(get_conditions(TRY, 0), CLIQUE, nr); + set_bit_array_from_gap_list(data->clique, include_obj); + complement_bit_arrays(get_conditions(data->try, 0), data->clique, nr); for (uint16_t i = 1; i <= LEN_LIST(include_obj); ++i) { - intersect_bit_arrays(get_conditions(TRY, 0), - GRAPH->neighbours[INT_INTOBJ(ELM_LIST(include_obj, i)) - 1], + intersect_bit_arrays(get_conditions(data->try, 0), + data->graph->neighbours[INT_INTOBJ(ELM_LIST(include_obj, i)) - 1], nr); } } @@ -220,15 +224,15 @@ static bool init_data_from_args(Obj digraph_obj, if (exclude_obj != Fail) { BitArray* exclude = new_bit_array(MAXVERTS); set_bit_array_from_gap_list(exclude, exclude_obj); - complement_bit_arrays(get_conditions(TRY, 0), exclude, nr); + complement_bit_arrays(get_conditions(data->try, 0), exclude, nr); } // Get the isolated vertices of the graph BitArray* isolated = new_bit_array(MAXVERTS); Int first_isolated = -1; for (uint16_t i = 0; i < nr; ++i) { - if (size_bit_array(GRAPH->neighbours[i], nr) == 0) { - if (first_isolated == -1 && get_bit_array(get_conditions(TRY, 0), i)) { + if (size_bit_array(data->graph->neighbours[i], nr) == 0) { + if (first_isolated == -1 && get_bit_array(get_conditions(data->try, 0), i)) { first_isolated = i; } set_bit_array(isolated, i, true); @@ -236,8 +240,8 @@ static bool init_data_from_args(Obj digraph_obj, } // Update TRY using isolated, only one isolated vertex is used if (first_isolated != -1) { - complement_bit_arrays(get_conditions(TRY, 0), isolated, nr); - set_bit_array(get_conditions(TRY, 0), first_isolated, true); + complement_bit_arrays(get_conditions(data->try, 0), isolated, nr); + set_bit_array(get_conditions(data->try, 0), first_isolated, true); } // Discard the generators of aut_grp_obj which act on the isolated vertices @@ -259,12 +263,13 @@ static bool init_data_from_args(Obj digraph_obj, } if (hook_obj != Fail) { - GAP_FUNC = hook_obj; - HOOK = clique_hook_gap; + data->gap_func = hook_obj; + data->hook = clique_hook_gap; } else { - HOOK = clique_hook_collect; + data->gap_func = Fail; + data->hook = clique_hook_collect; } - USER_PARAM = user_param_obj; + data->user_param = user_param_obj; return true; } @@ -273,49 +278,47 @@ static bool init_data_from_args(Obj digraph_obj, // Main functions //////////////////////////////////////////////////////////////////////////////// -static void BronKerbosch(uint16_t depth, - uint16_t rep_depth, - uint64_t limit, - uint64_t* nr_found, - bool max, - uint16_t size, - Obj group) { +static int BronKerbosch(uint16_t depth, + uint16_t rep_depth, + uint64_t limit, + uint64_t* nr_found, + bool max, + uint16_t size, + Obj group, + CliqueData* data) { - uint16_t nr = GRAPH->nr_vertices; - BitArray* try = get_conditions(TRY, 0); - BitArray* ban = get_conditions(BAN, 0); + uint16_t nr = data->graph->nr_vertices; + BitArray* try = get_conditions(data->try, 0); + BitArray* ban = get_conditions(data->ban, 0); if (depth > 0 && !max && ( size == 0 || size == depth)) { // We are not looking for maximal cliques - HOOK(USER_PARAM, CLIQUE, nr); + data->hook(data->user_param, data->clique, nr, data->gap_func); *nr_found += 1; if (*nr_found >= limit) { - longjmp(OUTOFHERE, 1); + return EXIT; } } else if (size_bit_array(try, nr) == 0 && size_bit_array(ban, nr) == 0 && (size == 0 || size == depth)) { // is a maximal clique - HOOK(USER_PARAM, CLIQUE, nr); + data->hook(data->user_param, data->clique, nr, data->gap_func); *nr_found += 1; if (*nr_found >= limit) { - longjmp(OUTOFHERE, 1); + return EXIT; } - return; } - // TODO: - BitArray* to_try = new_bit_array(MAXVERTS); + BitArray* to_try = get_conditions(data->to_try, 0); if (max) { // Choose a pivot with as many neighbours in as possible uint16_t pivot = 0; int16_t max_neighbours = -1; - BitArray* copy_try = new_bit_array(MAXVERTS); for (uint16_t i = 0; i < nr; i++){ if (get_bit_array(try, i) || get_bit_array(ban, i)){ - copy_bit_array(copy_try, try, nr); - intersect_bit_arrays(copy_try, GRAPH->neighbours[i], nr); - uint16_t num_neighbours = size_bit_array(copy_try, nr); + copy_bit_array(data->temp_bitarray, try, nr); + intersect_bit_arrays(data->temp_bitarray, data->graph->neighbours[i], nr); + uint16_t num_neighbours = size_bit_array(data->temp_bitarray, nr); if (num_neighbours > max_neighbours) { pivot = i; max_neighbours = num_neighbours; @@ -324,50 +327,59 @@ static void BronKerbosch(uint16_t depth, } // Try adding vertices from minus neighbours of - init_bit_array(to_try, 1, nr); - complement_bit_arrays(to_try, GRAPH->neighbours[pivot], nr); + init_bit_array(to_try, true, nr); + complement_bit_arrays(to_try, data->graph->neighbours[pivot], nr); intersect_bit_arrays(to_try, try, nr); } else { // If we are not looking for maximal cliques, a pivot cannot be used copy_bit_array(to_try, try, nr); } + // Update the height of the condition data->to_try, since we didn't use + // push_condition + data->to_try->height[0]++; // Get orbit representatives of - get_orbit_reps_bitarray(to_try, group); + get_orbit_reps_bitarray(to_try, group, data); for (uint16_t v = 0; v < nr; v++) { if (get_bit_array(to_try, v)){ - set_bit_array(CLIQUE, v, true); + set_bit_array(data->clique, v, true); - push_conditions(TRY, depth + 1, 0, GRAPH->neighbours[v]); - push_conditions(BAN, depth + 1, 0, GRAPH->neighbours[v]); + push_conditions(data->try, depth + 1, 0, data->graph->neighbours[v]); + push_conditions(data->ban, depth + 1, 0, data->graph->neighbours[v]); // recurse if (group == Fail) { - BronKerbosch(depth + 1, rep_depth, limit, nr_found, max, size, group); + if (EXIT == BronKerbosch(depth + 1, rep_depth, limit, nr_found, max, size, group, data)) { + return EXIT; + } } else { Obj stabiliser = CALL_2ARGS(Stabilizer, group, INTOBJ_INT(v + 1)); if (CALL_1ARGS(IsTrivial, stabiliser) == True) { stabiliser = Fail; } - BronKerbosch(depth + 1, rep_depth + 1, limit, nr_found, max, size, stabiliser); + if (EXIT == BronKerbosch(depth + 1, rep_depth + 1, limit, nr_found, max, size, stabiliser, data)) { + return EXIT; + } } - pop_conditions(TRY, depth + 1); - pop_conditions(BAN, depth + 1); - set_bit_array(CLIQUE, v, false); + pop_conditions(data->try, depth + 1); + pop_conditions(data->ban, depth + 1); + data->to_try->height[0]--; + set_bit_array(data->clique, v, false); if (group == Fail) { - set_bit_array(get_conditions(TRY, 0), v, false); - set_bit_array(get_conditions(BAN, 0), v, true); + set_bit_array(get_conditions(data->try, 0), v, false); + set_bit_array(get_conditions(data->ban, 0), v, true); } else { - ORBIT = CALL_2ARGS(Orbit, group, INTOBJ_INT(v + 1)); - set_bit_array_from_gap_list(ORB_LOOKUP, ORBIT); - complement_bit_arrays(get_conditions(TRY,0), ORB_LOOKUP, nr); - union_bit_arrays(get_conditions(BAN,0), ORB_LOOKUP, nr); + data->orbit = CALL_2ARGS(Orbit, group, INTOBJ_INT(v + 1)); + set_bit_array_from_gap_list(data->temp_bitarray, data->orbit); + complement_bit_arrays(get_conditions(data->try,0), data->temp_bitarray, nr); + union_bit_arrays(get_conditions(data->ban,0), data->temp_bitarray, nr); } } } + return EXIT + 1; } // FuncDigraphsCliquesFinder is the main function to use the C implementation @@ -461,9 +473,9 @@ Obj FuncDigraphsCliquesFinder(Obj self, Obj args) { for (Int i = 1; i <= LEN_LIST(include_obj); ++i) { if (!ISB_LIST(include_obj, i)) { ErrorQuit("the 5th argument must be a dense list,", 0L, 0L); - } else if (!IS_POS_INT(ELM_LIST(include_obj, i))) { + } else if (!IS_POS_INTOBJ(ELM_LIST(include_obj, i))) { ErrorQuit("the 5th argument must only contain positive " - "integers, but found %s in position %d,", + "small integers, but found %s in position %d,", (Int) TNAM_OBJ(ELM_LIST(include_obj, i)), i); } else if (INT_INTOBJ(ELM_LIST(include_obj, i)) @@ -487,9 +499,9 @@ Obj FuncDigraphsCliquesFinder(Obj self, Obj args) { for (Int i = 1; i <= LEN_LIST(exclude_obj); ++i) { if (!ISB_LIST(exclude_obj, i)) { ErrorQuit("the 6th argument must be a dense list,", 0L, 0L); - } else if (!IS_POS_INT(ELM_LIST(exclude_obj, i))) { + } else if (!IS_POS_INTOBJ(ELM_LIST(exclude_obj, i))) { ErrorQuit("the 6th argument must only contain positive " - "integers, but found %s in position %d,", + "small integers, but found %s in position %d,", (Int) TNAM_OBJ(ELM_LIST(exclude_obj, i)), i); } else if (INT_INTOBJ(ELM_LIST(exclude_obj, i)) @@ -510,17 +522,12 @@ Obj FuncDigraphsCliquesFinder(Obj self, Obj args) { (Int) TNAM_OBJ(max_obj), 0L); } - if (!IS_INTOBJ(size_obj) && size_obj != Fail) { - ErrorQuit("the 8th argument must be an integer " + if (!IS_POS_INTOBJ(size_obj) && size_obj != Fail) { + ErrorQuit("the 8th argument must be a positive small integer " "or fail, not %s,", (Int) TNAM_OBJ(size_obj), 0L); - } else if (IS_INTOBJ(size_obj) && INT_INTOBJ(size_obj) <= 0) { - ErrorQuit("the 8th argument must be a positive integer, " - "not %d,", - INT_INTOBJ(size_obj), - 0L); - } + } if (aut_grp_obj != Fail) { if (CALL_1ARGS(IsPermGroup, aut_grp_obj) != True) { @@ -531,8 +538,6 @@ Obj FuncDigraphsCliquesFinder(Obj self, Obj args) { 0L); } Obj gens = CALL_1ARGS(GeneratorsOfGroup, aut_grp_obj); - DIGRAPHS_ASSERT(IS_LIST(gens)); - DIGRAPHS_ASSERT(LEN_LIST(gens) > 0); UInt lmp = INT_INTOBJ(CALL_1ARGS(LargestMovedPointPerms, gens)); if (lmp > 0 && LEN_LIST(gens) >= lmp) { ErrorQuit("expected at most %d generators in the 9th argument " @@ -541,7 +546,7 @@ Obj FuncDigraphsCliquesFinder(Obj self, Obj args) { LEN_LIST(gens)); } for (UInt i = 1; i <= LEN_LIST(gens); ++i) { - if (CALL_2ARGS(IsDigraphAutomorphism, digraph_obj, ELM_LIST(gens, i)) + if (CALL_2ARGS(IsDigraphAutomorphism, digraph_obj, ELM_LIST(gens, i)) != True) { ErrorQuit("expected group of automorphisms, but found a " "non-automorphism in position %d of the group generators,", @@ -603,6 +608,8 @@ Obj FuncDigraphsCliquesFinder(Obj self, Obj args) { uint64_t nr_found = 0; uint64_t limit = (limit_obj == Infinity ? SMALLINTLIMIT : INT_INTOBJ(limit_obj)); bool max = (max_obj == True ? true : false); + + static CliqueData data = {}; // Initialise all the variable which will be used to carry out the recursion if (!init_data_from_args(digraph_obj, hook_obj, @@ -610,19 +617,18 @@ Obj FuncDigraphsCliquesFinder(Obj self, Obj args) { include_obj, exclude_obj, max_obj, - &aut_grp_obj)) { + &aut_grp_obj, + &data)) { return user_param_obj; } // The clique we are trying to extend is already big enough if (size != 0 && include_size == size) { - HOOK(USER_PARAM, CLIQUE, nr); + data.hook(data.user_param, data.clique, nr, data.gap_func); return user_param_obj; } // go! - if (setjmp(OUTOFHERE) == 0) { - BronKerbosch(0, 0, limit, &nr_found, max, (size == 0 ? size : size - include_size), aut_grp_obj); - } + BronKerbosch(0, 0, limit, &nr_found, max, (size == 0 ? size : size - include_size), aut_grp_obj, &data); return user_param_obj; } From 7da97917457bb460d6b2c2fca4028edc50f4d361 Mon Sep 17 00:00:00 2001 From: Julius Date: Tue, 26 May 2020 14:19:59 +0200 Subject: [PATCH 25/30] fix: fixed an issue with limit not being used correctly --- doc/cliques.xml | 6 +++++- gap/cliques.gi | 53 ++++++++++++++++++++++++++----------------------- src/cliques.c | 35 ++++++++++++++++++-------------- 3 files changed, 53 insertions(+), 41 deletions(-) diff --git a/doc/cliques.xml b/doc/cliques.xml index eac68d714..583bca84f 100644 --- a/doc/cliques.xml +++ b/doc/cliques.xml @@ -552,7 +552,9 @@ gap> DigraphIndependentSets(D, [], [4, 5], 1, 2); If hook is a function, then it should have two arguments user_param (see below) and a clique c. The function hook(user_param, c) is called every time a new - clique c is found by CliquesFinder.

+ clique c is found by CliquesFinder and should return a + number of cliques obtained from c (for example, by applying + automorphisms of G).

If hook is fail, then a default function is used that simply adds every new clique found by CliquesFinder to @@ -634,6 +636,7 @@ gap> D := CompleteDigraph(5); gap> user_param := [];; gap> f := function(a, b) # Calculate size of clique > AddSet(user_param, Size(b)); +> return 1; > end;; gap> CliquesFinder(D, f, user_param, infinity, [], [], false, fail, > true); @@ -653,6 +656,7 @@ gap> D := CompleteDigraph(IsMutableDigraph, 5); gap> user_param := [];; gap> f := function(a, b) # Calculate size of clique > AddSet(user_param, Size(b)); +> return 1; > end;; gap> CliquesFinder(D, f, user_param, infinity, [], [], false, fail, > true); diff --git a/gap/cliques.gi b/gap/cliques.gi index b76605a11..e567e2723 100644 --- a/gap/cliques.gi +++ b/gap/cliques.gi @@ -546,9 +546,10 @@ end); # This is very hacky at the moment, so we could test C code with GAP tests InstallGlobalFunction(CliquesFinder, function(digraph, hook, user_param, limit, include, exclude, max, size, reps) - local n, subgraph, group, vertices, invariant_include, invariant_exclude, - include_variant, exclude_variant, include_invariant, exclude_invariant, - x, v, o, i, out, out_reps, found_orbits, num_found, add_cliques; + local n, subgraph, group, vertices, include_variant, exclude_variant, + invariant_include, include_invariant, invariant_exclude, + exclude_invariant, x, v, o, i, out, found_orbits, num_found, + hook_wrapper; if not IsDigraph(digraph) then ErrorNoReturn("the 1st argument must be a digraph,"); @@ -670,14 +671,6 @@ function(digraph, hook, user_param, limit, include, exclude, max, size, reps) size); return MakeImmutable(out); else - out_reps := DigraphsCliquesFinder(subgraph, - fail, - [], - limit, - include_invariant, - exclude_invariant, - max, - size); # Function to find the valid cliques of an orbit given an orbit rep found_orbits := []; @@ -686,9 +679,10 @@ function(digraph, hook, user_param, limit, include, exclude, max, size, reps) hook := Add; fi; - add_cliques := function(clique) - local orbit, n, i; + hook_wrapper := function(usr_param, clique) + local orbit, n, new_found, i; + new_found := 0; if not ForAny(found_orbits, x -> clique in x) then orbit := Orb(group, clique, OnSets); Enumerate(orbit); @@ -698,12 +692,12 @@ function(digraph, hook, user_param, limit, include, exclude, max, size, reps) if invariant_include and invariant_exclude then # we're not just looking for orbit reps, but inc and exc are invariant # so there is nothing extra to check - n := Minimum(limit - num_found, n); - for clique in orbit{[1 .. n]} do - hook(user_param, clique); + new_found := Minimum(limit - num_found, n); + for clique in orbit{[1 .. new_found]} do + hook(usr_param, clique); od; - num_found := num_found + n; - return; + num_found := num_found + new_found; + return new_found; fi; if invariant_include then @@ -713,8 +707,9 @@ function(digraph, hook, user_param, limit, include, exclude, max, size, reps) i := i + 1; clique := BlistList(vertices, orbit[i]); if SizeBlist(IntersectionBlist(exclude_variant, clique)) = 0 then - hook(user_param, orbit[i]); + hook(usr_param, orbit[i]); num_found := num_found + 1; + new_found := new_found + 1; fi; od; elif invariant_exclude then @@ -724,8 +719,9 @@ function(digraph, hook, user_param, limit, include, exclude, max, size, reps) i := i + 1; clique := BlistList(vertices, orbit[i]); if IsSubsetBlist(clique, include_variant) then - hook(user_param, orbit[i]); + hook(usr_param, orbit[i]); num_found := num_found + 1; + new_found := new_found + 1; fi; od; else @@ -737,18 +733,25 @@ function(digraph, hook, user_param, limit, include, exclude, max, size, reps) clique := BlistList(vertices, orbit[i]); if SizeBlist(IntersectionBlist(exclude_variant, clique)) = 0 and IsSubsetBlist(clique, include_variant) then - hook(user_param, orbit[i]); + hook(usr_param, orbit[i]); num_found := num_found + 1; + new_found := new_found + 1; fi; od; fi; fi; - return; + return new_found; end; - for x in out_reps do - add_cliques(x); - od; + DigraphsCliquesFinder(subgraph, + hook_wrapper, + user_param, + limit, + include_invariant, + exclude_invariant, + max, + size); + return MakeImmutable(user_param); fi; else diff --git a/src/cliques.c b/src/cliques.c index 234b763ca..3ff7913d0 100644 --- a/src/cliques.c +++ b/src/cliques.c @@ -64,7 +64,7 @@ extern Obj ClosureGroup; struct clique_data { void* user_param; // A USER_PARAM for the hook Obj gap_func; // Variable to hold a GAP level hook function - Obj (*hook)(void*, // HOOK function applied to every homo found + UInt (*hook)(void*, // HOOK function applied to every homo found const BitArray*, const uint16_t, Obj); @@ -85,10 +85,10 @@ typedef struct clique_data CliqueData; // Hook functions //////////////////////////////////////////////////////////////////////////////// -static Obj clique_hook_collect(void* user_param, - const BitArray* clique, - const uint16_t nr, - Obj gap_func) { +static UInt clique_hook_collect(void* user_param, + const BitArray* clique, + const uint16_t nr, + Obj gap_func) { UInt i; Obj c; @@ -100,21 +100,28 @@ static Obj clique_hook_collect(void* user_param, } ASS_LIST(user_param, LEN_LIST(user_param) + 1, c); - return False; + return 1; } -static Obj clique_hook_gap(void* user_param, const BitArray* clique, const uint16_t nr, Obj gap_func) { - UInt i; - Obj c; +static UInt clique_hook_gap(void* user_param, const BitArray* clique, const uint16_t nr, Obj gap_func) { + UInt i; + Obj c; + Obj n; c = NEW_PLIST(T_PLIST, nr); for(i = 1; i <= nr; i++) { if (get_bit_array(clique, i-1)) { - PushPlist(c, INTOBJ_INT(i + 1)); + PushPlist(c, INTOBJ_INT(i)); } } - return CALL_2ARGS(gap_func, user_param, c); + n = CALL_2ARGS(gap_func, user_param, c); + if (!IS_INTOBJ(n)) { + ErrorQuit( + "the 2rd argument must be a function which returns " + "an integer,", 0L, 0L); + } + return INT_INTOBJ(n); } //////////////////////////////////////////////////////////////////////////////// @@ -293,16 +300,14 @@ static int BronKerbosch(uint16_t depth, if (depth > 0 && !max && ( size == 0 || size == depth)) { // We are not looking for maximal cliques - data->hook(data->user_param, data->clique, nr, data->gap_func); - *nr_found += 1; + *nr_found += data->hook(data->user_param, data->clique, nr, data->gap_func); if (*nr_found >= limit) { return EXIT; } } else if (size_bit_array(try, nr) == 0 && size_bit_array(ban, nr) == 0 && (size == 0 || size == depth)) { // is a maximal clique - data->hook(data->user_param, data->clique, nr, data->gap_func); - *nr_found += 1; + *nr_found += data->hook(data->user_param, data->clique, nr, data->gap_func); if (*nr_found >= limit) { return EXIT; } From 2f76d12d5912b4ed53887ac96ea6c8f711557928 Mon Sep 17 00:00:00 2001 From: Julius Date: Tue, 26 May 2020 14:42:27 +0200 Subject: [PATCH 26/30] formating/linting --- src/cliques.c | 384 ++++++++++++++++++++++++++++---------------------- 1 file changed, 217 insertions(+), 167 deletions(-) diff --git a/src/cliques.c b/src/cliques.c index 3ff7913d0..9b40df3a2 100644 --- a/src/cliques.c +++ b/src/cliques.c @@ -2,10 +2,12 @@ // // cliques.c cliques Julius Jonusas // -// Copyright (C) 2020 - Julius Jonusas +// Copyright (C) 2020 - Julius Jonusas // // This file is free software, see the digraphs/LICENSE. +#include + // C headers #include // for true, false, bool #include // for NULL @@ -16,11 +18,11 @@ #include "src/compiled.h" // Digraphs package headers -#include "bitarray.h" // for BitArray -#include "conditions.h" // for Conditions -#include "digraphs-debug.h" // for DIGRAPHS_ASSERT -#include "homos-graphs.h" // for Digraph, Graph, . . . -#include "perms.h" // for MAXVERTS, UNDEFINED, PermColl, Perm +#include "bitarray.h" // for BitArray +#include "conditions.h" // for Conditions +#include "digraphs-debug.h" // for DIGRAPHS_ASSERT +#include "homos-graphs.h" // for Digraph, Graph, . . . +#include "perms.h" // for MAXVERTS, UNDEFINED, PermColl, Perm //////////////////////////////////////////////////////////////////////////////// // Macros @@ -62,21 +64,22 @@ extern Obj ClosureGroup; //////////////////////////////////////////////////////////////////////////////// struct clique_data { - void* user_param; // A USER_PARAM for the hook - Obj gap_func; // Variable to hold a GAP level hook function + void* user_param; // A USER_PARAM for the hook + Obj gap_func; // Variable to hold a GAP level hook function UInt (*hook)(void*, // HOOK function applied to every homo found - const BitArray*, - const uint16_t, - Obj); - - Graph* graph; // Graphs to hold incoming GAP symmetric digraphs - BitArray* clique; - Conditions* try; + const BitArray*, + const uint16_t, + Obj); + + Graph* graph; // Graphs to hold incoming GAP symmetric digraphs + BitArray* clique; + Conditions* ban; Conditions* to_try; - - BitArray* temp_bitarray; - Obj orbit; + Conditions* try; + + BitArray* temp_bitarray; + Obj orbit; }; typedef struct clique_data CliqueData; @@ -85,16 +88,16 @@ typedef struct clique_data CliqueData; // Hook functions //////////////////////////////////////////////////////////////////////////////// -static UInt clique_hook_collect(void* user_param, +static UInt clique_hook_collect(void* user_param, const BitArray* clique, - const uint16_t nr, - Obj gap_func) { - UInt i; - Obj c; - - c = NEW_PLIST(T_PLIST, nr); - for(i = 1; i <= nr; i++) { - if (get_bit_array(clique, i - 1)){ + const uint16_t nr, + Obj gap_func) { + UInt i; + Obj c; + + c = NEW_PLIST(T_PLIST, nr); + for (i = 1; i <= nr; ++i) { + if (get_bit_array(clique, i - 1)) { PushPlist(c, INTOBJ_INT(i)); } } @@ -103,23 +106,27 @@ static UInt clique_hook_collect(void* user_param, return 1; } -static UInt clique_hook_gap(void* user_param, const BitArray* clique, const uint16_t nr, Obj gap_func) { +static UInt clique_hook_gap(void* user_param, + const BitArray* clique, + const uint16_t nr, + Obj gap_func) { UInt i; Obj c; Obj n; - - c = NEW_PLIST(T_PLIST, nr); - for(i = 1; i <= nr; i++) { - if (get_bit_array(clique, i-1)) { + + c = NEW_PLIST(T_PLIST, nr); + for (i = 1; i <= nr; ++i) { + if (get_bit_array(clique, i - 1)) { PushPlist(c, INTOBJ_INT(i)); } } n = CALL_2ARGS(gap_func, user_param, c); - if (!IS_INTOBJ(n)) { - ErrorQuit( - "the 2rd argument must be a function which returns " - "an integer,", 0L, 0L); + if (!IS_INTOBJ(n)) { + ErrorQuit("the 2rd argument must be a function which returns " + "an integer,", + 0L, + 0L); } return INT_INTOBJ(n); } @@ -129,25 +136,26 @@ static UInt clique_hook_gap(void* user_param, const BitArray* clique, const uint //////////////////////////////////////////////////////////////////////////////// // Update a BitArray to only include one vertex per orbit with respect to -// the group -static void get_orbit_reps_bitarray(BitArray* bit_array, - Obj const group, +// the group +static void get_orbit_reps_bitarray(BitArray* bit_array, + Obj const group, CliqueData* data) { if (group == Fail) { return; } uint16_t nr = data->graph->nr_vertices; - for (uint16_t v = 0; v < nr; ++v) { + for (uint16_t v = 0; v < nr; ++v) { if (get_bit_array(bit_array, v)) { // Find the orbit of pt and remove all other points of the orbit from // - + data->orbit = CALL_2ARGS(Orbit, group, INTOBJ_INT(v + 1)); DIGRAPHS_ASSERT(IS_LIST(data->orbit)); for (Int i = 1; i <= LEN_LIST(data->orbit); ++i) { - set_bit_array(bit_array, INT_INTOBJ(ELM_LIST(data->orbit, i)) - 1, false); + set_bit_array( + bit_array, INT_INTOBJ(ELM_LIST(data->orbit, i)) - 1, false); } set_bit_array(bit_array, v, true); } @@ -156,25 +164,25 @@ static void get_orbit_reps_bitarray(BitArray* bit_array, // Initialise the graph from GAP digraph and discard non-symmetric edges and // loops -static void init_graph_from_digraph_obj(Graph* const graph, - Obj digraph_obj) { +static void init_graph_from_digraph_obj(Graph* const graph, Obj digraph_obj) { DIGRAPHS_ASSERT(graph != NULL); DIGRAPHS_ASSERT(CALL_1ARGS(IsDigraph, digraph_obj) == True); - UInt const nr = DigraphNrVertices(digraph_obj); - Obj out = FuncOutNeighbours(0L, digraph_obj); - Obj adj_mat = FuncADJACENCY_MATRIX(0L, digraph_obj); + UInt const nr = DigraphNrVertices(digraph_obj); + Obj out = FuncOutNeighbours(0L, digraph_obj); + Obj adj_mat = FuncADJACENCY_MATRIX(0L, digraph_obj); DIGRAPHS_ASSERT(nr < MAXVERTS); DIGRAPHS_ASSERT(IS_PLIST(adj_mat)); DIGRAPHS_ASSERT(IS_PLIST(out)); clear_graph(graph, nr); // Only include symmetric edges - for (Int i = 1; i < nr; i++) { + for (Int i = 1; i < nr; ++i) { Obj row = ELM_PLIST(adj_mat, i); DIGRAPHS_ASSERT(IS_LIST(row)); Obj zero_obj = INTOBJ_INT(0); - for (Int j = i + 1; j <= nr; j++) { - if (ELM_PLIST(row, j) != zero_obj && ELM_PLIST(ELM_PLIST(adj_mat,j) ,i) != zero_obj){ + for (Int j = i + 1; j <= nr; ++j) { + if (ELM_PLIST(row, j) != zero_obj + && ELM_PLIST(ELM_PLIST(adj_mat, j), i) != zero_obj) { add_edge_graph(graph, i - 1, j - 1); add_edge_graph(graph, j - 1, i - 1); } @@ -183,15 +191,15 @@ static void init_graph_from_digraph_obj(Graph* const graph, } // Initialise the global variables -static bool init_data_from_args(Obj digraph_obj, - Obj hook_obj, - Obj user_param_obj, - Obj include_obj, - Obj exclude_obj, - Obj max_obj, - Obj* group, - CliqueData* data) { - static bool is_initialised = false; +static bool init_data_from_args(Obj digraph_obj, + Obj hook_obj, + Obj user_param_obj, + Obj include_obj, + Obj exclude_obj, + Obj max_obj, + Obj* group, + CliqueData* data) { + static bool is_initialised = false; if (!is_initialised) { is_initialised = true; @@ -199,12 +207,13 @@ static bool init_data_from_args(Obj digraph_obj, // Currently Conditions are a nr1 x nr1 array of BitArrays, so both // values have to be set to MAXVERTS - data->clique = new_bit_array(MAXVERTS); - data->try = new_conditions(MAXVERTS, MAXVERTS); - data->ban = new_conditions(MAXVERTS, MAXVERTS); - data->to_try = new_conditions(MAXVERTS, MAXVERTS); + data->clique = new_bit_array(MAXVERTS); + data->try + = new_conditions(MAXVERTS, MAXVERTS); + data->ban = new_conditions(MAXVERTS, MAXVERTS); + data->to_try = new_conditions(MAXVERTS, MAXVERTS); - data->orbit = Fail; + data->orbit = Fail; data->temp_bitarray = new_bit_array(MAXVERTS); } @@ -222,62 +231,64 @@ static bool init_data_from_args(Obj digraph_obj, set_bit_array_from_gap_list(data->clique, include_obj); complement_bit_arrays(get_conditions(data->try, 0), data->clique, nr); for (uint16_t i = 1; i <= LEN_LIST(include_obj); ++i) { - intersect_bit_arrays(get_conditions(data->try, 0), - data->graph->neighbours[INT_INTOBJ(ELM_LIST(include_obj, i)) - 1], - nr); + intersect_bit_arrays( + get_conditions(data->try, 0), + data->graph->neighbours[INT_INTOBJ(ELM_LIST(include_obj, i)) - 1], + nr); } } // Update TRY using exclude_obj if (exclude_obj != Fail) { - BitArray* exclude = new_bit_array(MAXVERTS); + BitArray* exclude = new_bit_array(MAXVERTS); set_bit_array_from_gap_list(exclude, exclude_obj); complement_bit_arrays(get_conditions(data->try, 0), exclude, nr); } - + // Get the isolated vertices of the graph - BitArray* isolated = new_bit_array(MAXVERTS); - Int first_isolated = -1; + BitArray* isolated = new_bit_array(MAXVERTS); + Int first_isolated = -1; for (uint16_t i = 0; i < nr; ++i) { if (size_bit_array(data->graph->neighbours[i], nr) == 0) { - if (first_isolated == -1 && get_bit_array(get_conditions(data->try, 0), i)) { + if (first_isolated == -1 + && get_bit_array(get_conditions(data->try, 0), i)) { first_isolated = i; } set_bit_array(isolated, i, true); } } // Update TRY using isolated, only one isolated vertex is used - if (first_isolated != -1) { + if (first_isolated != -1) { complement_bit_arrays(get_conditions(data->try, 0), isolated, nr); set_bit_array(get_conditions(data->try, 0), first_isolated, true); } // Discard the generators of aut_grp_obj which act on the isolated vertices if (size_bit_array(isolated, nr) > 0) { - Obj new_group = Fail; - Obj gens = CALL_1ARGS(GeneratorsOfGroup, *group); + Obj new_group = Fail; + Obj gens = CALL_1ARGS(GeneratorsOfGroup, *group); DIGRAPHS_ASSERT(IS_LIST(gens)); for (Int i = 1; i <= LEN_LIST(gens); ++i) { Obj s = CALL_1ARGS(SmallestMovedPointPerm, ELM_LIST(gens, i)); - if (s != Infinity && !get_bit_array(isolated, INT_INTOBJ(s) - 1)) { + if (s != Infinity && !get_bit_array(isolated, INT_INTOBJ(s) - 1)) { if (new_group == Fail) { new_group = CALL_1ARGS(Group, ELM_LIST(gens, i)); - } else { + } else { new_group = CALL_2ARGS(ClosureGroup, new_group, ELM_LIST(gens, i)); } } } *group = new_group; } - + if (hook_obj != Fail) { data->gap_func = hook_obj; data->hook = clique_hook_gap; } else { data->gap_func = Fail; - data->hook = clique_hook_collect; + data->hook = clique_hook_collect; } data->user_param = user_param_obj; - + return true; } @@ -285,59 +296,60 @@ static bool init_data_from_args(Obj digraph_obj, // Main functions //////////////////////////////////////////////////////////////////////////////// -static int BronKerbosch(uint16_t depth, - uint16_t rep_depth, - uint64_t limit, - uint64_t* nr_found, - bool max, - uint16_t size, - Obj group, - CliqueData* data) { - +static int BronKerbosch(uint16_t depth, + uint16_t rep_depth, + uint64_t limit, + uint64_t* nr_found, + bool max, + uint16_t size, + Obj group, + CliqueData* data) { uint16_t nr = data->graph->nr_vertices; - BitArray* try = get_conditions(data->try, 0); + BitArray* try + = get_conditions(data->try, 0); BitArray* ban = get_conditions(data->ban, 0); - if (depth > 0 && !max && ( size == 0 || size == depth)) { + if (depth > 0 && !max && (size == 0 || size == depth)) { // We are not looking for maximal cliques *nr_found += data->hook(data->user_param, data->clique, nr, data->gap_func); if (*nr_found >= limit) { return EXIT; } - } else if (size_bit_array(try, nr) == 0 && size_bit_array(ban, nr) == 0 && - (size == 0 || size == depth)) { + } else if (size_bit_array(try, nr) == 0 && size_bit_array(ban, nr) == 0 + && (size == 0 || size == depth)) { // is a maximal clique *nr_found += data->hook(data->user_param, data->clique, nr, data->gap_func); if (*nr_found >= limit) { return EXIT; } - } - + } + BitArray* to_try = get_conditions(data->to_try, 0); if (max) { - // Choose a pivot with as many neighbours in as possible - uint16_t pivot = 0; - int16_t max_neighbours = -1; + // Choose a pivot with as many neighbours in as possible + uint16_t pivot = 0; + int16_t max_neighbours = -1; - for (uint16_t i = 0; i < nr; i++){ - if (get_bit_array(try, i) || get_bit_array(ban, i)){ + for (uint16_t i = 0; i < nr; ++i) { + if (get_bit_array(try, i) || get_bit_array(ban, i)) { copy_bit_array(data->temp_bitarray, try, nr); - intersect_bit_arrays(data->temp_bitarray, data->graph->neighbours[i], nr); + intersect_bit_arrays( + data->temp_bitarray, data->graph->neighbours[i], nr); uint16_t num_neighbours = size_bit_array(data->temp_bitarray, nr); if (num_neighbours > max_neighbours) { - pivot = i; + pivot = i; max_neighbours = num_neighbours; } } } - // Try adding vertices from minus neighbours of + // Try adding vertices from minus neighbours of init_bit_array(to_try, true, nr); complement_bit_arrays(to_try, data->graph->neighbours[pivot], nr); - intersect_bit_arrays(to_try, try, nr); + intersect_bit_arrays(to_try, try, nr); } else { // If we are not looking for maximal cliques, a pivot cannot be used - copy_bit_array(to_try, try, nr); + copy_bit_array(to_try, try, nr); } // Update the height of the condition data->to_try, since we didn't use // push_condition @@ -346,8 +358,8 @@ static int BronKerbosch(uint16_t depth, // Get orbit representatives of get_orbit_reps_bitarray(to_try, group, data); - for (uint16_t v = 0; v < nr; v++) { - if (get_bit_array(to_try, v)){ + for (uint16_t v = 0; v < nr; ++v) { + if (get_bit_array(to_try, v)) { set_bit_array(data->clique, v, true); push_conditions(data->try, depth + 1, 0, data->graph->neighbours[v]); @@ -355,15 +367,31 @@ static int BronKerbosch(uint16_t depth, // recurse if (group == Fail) { - if (EXIT == BronKerbosch(depth + 1, rep_depth, limit, nr_found, max, size, group, data)) { + if (EXIT + == BronKerbosch(depth + 1, + rep_depth, + limit, + nr_found, + max, + size, + group, + data)) { return EXIT; } } else { Obj stabiliser = CALL_2ARGS(Stabilizer, group, INTOBJ_INT(v + 1)); - if (CALL_1ARGS(IsTrivial, stabiliser) == True) { + if (CALL_1ARGS(IsTrivial, stabiliser) == True) { stabiliser = Fail; } - if (EXIT == BronKerbosch(depth + 1, rep_depth + 1, limit, nr_found, max, size, stabiliser, data)) { + if (EXIT + == BronKerbosch(depth + 1, + rep_depth + 1, + limit, + nr_found, + max, + size, + stabiliser, + data)) { return EXIT; } } @@ -373,14 +401,15 @@ static int BronKerbosch(uint16_t depth, data->to_try->height[0]--; set_bit_array(data->clique, v, false); - if (group == Fail) { + if (group == Fail) { set_bit_array(get_conditions(data->try, 0), v, false); set_bit_array(get_conditions(data->ban, 0), v, true); } else { data->orbit = CALL_2ARGS(Orbit, group, INTOBJ_INT(v + 1)); - set_bit_array_from_gap_list(data->temp_bitarray, data->orbit); - complement_bit_arrays(get_conditions(data->try,0), data->temp_bitarray, nr); - union_bit_arrays(get_conditions(data->ban,0), data->temp_bitarray, nr); + set_bit_array_from_gap_list(data->temp_bitarray, data->orbit); + complement_bit_arrays( + get_conditions(data->try, 0), data->temp_bitarray, nr); + union_bit_arrays(get_conditions(data->ban, 0), data->temp_bitarray, nr); } } } @@ -389,7 +418,7 @@ static int BronKerbosch(uint16_t depth, // FuncDigraphsCliquesFinder is the main function to use the C implementation // of Bron-Kerbosch algorithm -// +// // The arguments are as follows: // // 1. digraphs_obj the digraph to search for cliques in @@ -398,7 +427,8 @@ static int BronKerbosch(uint16_t depth, // 3. user_param_obj GAP variable that can be used in the hook_obj, must be a // plist if hook_obj is Fail // 4. limit_obj the maximum number of cliques to find -// 5. include_obj a list of vertices of digraph_obj to required to be present in +// 5. include_obj a list of vertices of digraph_obj to required to be +// present in // every clique found. The list needs to be invariant under // aut_grp_obj or the full automorphism group if aut_grp_obj // is Fail @@ -414,15 +444,15 @@ static int BronKerbosch(uint16_t depth, // If not given, the full automorphism group will be used. // // Remarks: -// 1. The function returns orbit representatives of cliques rather than all of the -// cliques themselves. +// 1. The function returns orbit representatives of cliques rather than all of +// the +// cliques themselves. // 2. Only one isolated vertex will be returned even if aut_grp_obj does not -// act transitevely on all isolated vertices. +// act transitevely on all isolated vertices. Obj FuncDigraphsCliquesFinder(Obj self, Obj args) { - if (LEN_PLIST(args) != 8 && LEN_PLIST(args) != 9) { - ErrorQuit( - "there must be 8 or 9 arguments, found %d,", LEN_PLIST(args), 0L); + if (LEN_PLIST(args) != 8 && LEN_PLIST(args) != 9) { + ErrorQuit("there must be 8 or 9 arguments, found %d,", LEN_PLIST(args), 0L); } Obj digraph_obj = ELM_PLIST(args, 1); Obj hook_obj = ELM_PLIST(args, 2); @@ -483,16 +513,17 @@ Obj FuncDigraphsCliquesFinder(Obj self, Obj args) { "small integers, but found %s in position %d,", (Int) TNAM_OBJ(ELM_LIST(include_obj, i)), i); - } else if (INT_INTOBJ(ELM_LIST(include_obj, i)) + } else if (INT_INTOBJ(ELM_LIST(include_obj, i)) > DigraphNrVertices(digraph_obj)) { ErrorQuit("in the 5th argument position %d is out of range, " "must be in the range [1, %d],", i, DigraphNrVertices(digraph_obj)); - } else if (INT_INTOBJ( - POS_LIST(include_obj, ELM_LIST(include_obj, i), INTOBJ_INT(0))) + } else if (INT_INTOBJ(POS_LIST( + include_obj, ELM_LIST(include_obj, i), INTOBJ_INT(0))) < i) { - ErrorQuit("in the 5th argument position %d is a duplicate,", i, 0L); + ErrorQuit( + "in the 5th argument position %d is a duplicate,", i, 0L); } } } @@ -509,16 +540,17 @@ Obj FuncDigraphsCliquesFinder(Obj self, Obj args) { "small integers, but found %s in position %d,", (Int) TNAM_OBJ(ELM_LIST(exclude_obj, i)), i); - } else if (INT_INTOBJ(ELM_LIST(exclude_obj, i)) + } else if (INT_INTOBJ(ELM_LIST(exclude_obj, i)) > DigraphNrVertices(digraph_obj)) { ErrorQuit("in the 6th argument position %d is out of range, " "must be in the range [1, %d],", i, DigraphNrVertices(digraph_obj)); - } else if (INT_INTOBJ( - POS_LIST(exclude_obj, ELM_LIST(exclude_obj, i), INTOBJ_INT(0))) + } else if (INT_INTOBJ(POS_LIST( + exclude_obj, ELM_LIST(exclude_obj, i), INTOBJ_INT(0))) < i) { - ErrorQuit("in the 6th argument position %d is a duplicate,", i, 0L); + ErrorQuit( + "in the 6th argument position %d is a duplicate,", i, 0L); } } } @@ -532,18 +564,17 @@ Obj FuncDigraphsCliquesFinder(Obj self, Obj args) { "or fail, not %s,", (Int) TNAM_OBJ(size_obj), 0L); - } + } if (aut_grp_obj != Fail) { if (CALL_1ARGS(IsPermGroup, aut_grp_obj) != True) { - ErrorQuit( - "the 9th argument must be a permutation group " - "or fail, not %s,", - (Int) TNAM_OBJ(aut_grp_obj), - 0L); + ErrorQuit("the 9th argument must be a permutation group " + "or fail, not %s,", + (Int) TNAM_OBJ(aut_grp_obj), + 0L); } - Obj gens = CALL_1ARGS(GeneratorsOfGroup, aut_grp_obj); - UInt lmp = INT_INTOBJ(CALL_1ARGS(LargestMovedPointPerms, gens)); + Obj gens = CALL_1ARGS(GeneratorsOfGroup, aut_grp_obj); + UInt lmp = INT_INTOBJ(CALL_1ARGS(LargestMovedPointPerms, gens)); if (lmp > 0 && LEN_LIST(gens) >= lmp) { ErrorQuit("expected at most %d generators in the 9th argument " "but got %d,", @@ -551,12 +582,12 @@ Obj FuncDigraphsCliquesFinder(Obj self, Obj args) { LEN_LIST(gens)); } for (UInt i = 1; i <= LEN_LIST(gens); ++i) { - if (CALL_2ARGS(IsDigraphAutomorphism, digraph_obj, ELM_LIST(gens, i)) - != True) { - ErrorQuit("expected group of automorphisms, but found a " - "non-automorphism in position %d of the group generators,", - i, - 0L); + if (CALL_2ARGS(IsDigraphAutomorphism, digraph_obj, ELM_LIST(gens, i)) + != True) { + ErrorQuit("expected group of automorphisms, but found a " + "non-automorphism in position %d of the group generators,", + i, + 0L); } } } else { @@ -568,28 +599,38 @@ Obj FuncDigraphsCliquesFinder(Obj self, Obj args) { DIGRAPHS_ASSERT(IS_LIST(gens)); DIGRAPHS_ASSERT(LEN_LIST(gens) > 0); for (UInt i = 1; i <= LEN_LIST(gens); ++i) { - if (include_obj != Fail && CALL_2ARGS(IsSubset, include_obj, - CALL_2ARGS(OnTuples, include_obj, ELM_LIST(gens, i))) != True) { + if (include_obj != Fail + && CALL_2ARGS(IsSubset, + include_obj, + CALL_2ARGS(OnTuples, include_obj, ELM_LIST(gens, i))) + != True) { ErrorQuit("the 5th argument must be invaraint under , " - "or the full automorphism if is not given", 0L, 0L); + "or the full automorphism if is not given", + 0L, + 0L); } - if (exclude_obj != Fail && CALL_2ARGS(IsSubset, exclude_obj, - CALL_2ARGS(OnTuples, exclude_obj, ELM_LIST(gens, i))) != True) { + if (exclude_obj != Fail + && CALL_2ARGS(IsSubset, + exclude_obj, + CALL_2ARGS(OnTuples, exclude_obj, ELM_LIST(gens, i))) + != True) { ErrorQuit("the 6th argument must be invaraint under , " - "or the full automorphism if is not given", 0L, 0L); + "or the full automorphism if is not given", + 0L, + 0L); } } - - uint16_t size = (size_obj == Fail ? 0 : INT_INTOBJ(size_obj)); + + uint16_t size = (size_obj == Fail ? 0 : INT_INTOBJ(size_obj)); uint16_t include_size = (include_obj == Fail ? 0 : LEN_LIST(include_obj)); uint16_t exclude_size = (exclude_obj == Fail ? 0 : LEN_LIST(exclude_obj)); - uint16_t nr = DigraphNrVertices(digraph_obj); + uint16_t nr = DigraphNrVertices(digraph_obj); // Check the trivial cases: // The desired clique is too small - if (size != 0 && include_size > size) { - return user_param_obj; - } + if (size != 0 && include_size > size) { + return user_param_obj; + } // The desired clique is too big if (size != 0 && size > nr - exclude_size) { return user_param_obj; @@ -601,29 +642,31 @@ Obj FuncDigraphsCliquesFinder(Obj self, Obj args) { lookup[INT_INTOBJ(ELM_LIST(include_obj, i)) - 1] = true; } for (uint16_t i = 0; i < exclude_size; ++i) { - if (lookup[INT_INTOBJ(ELM_LIST(exclude_obj, i)) - 1]) { + if (lookup[INT_INTOBJ(ELM_LIST(exclude_obj, i)) - 1]) { return user_param_obj; } } } // Check if the set we are trying to extend is a clique - if (include_obj != Fail && CALL_2ARGS(IsClique, digraph_obj, include_obj) == False) { + if (include_obj != Fail + && CALL_2ARGS(IsClique, digraph_obj, include_obj) == False) { return user_param_obj; } uint64_t nr_found = 0; - uint64_t limit = (limit_obj == Infinity ? SMALLINTLIMIT : INT_INTOBJ(limit_obj)); - bool max = (max_obj == True ? true : false); + uint64_t limit = + (limit_obj == Infinity ? SMALLINTLIMIT : INT_INTOBJ(limit_obj)); + bool max = (max_obj == True ? true : false); static CliqueData data = {}; // Initialise all the variable which will be used to carry out the recursion - if (!init_data_from_args(digraph_obj, - hook_obj, - user_param_obj, - include_obj, - exclude_obj, + if (!init_data_from_args(digraph_obj, + hook_obj, + user_param_obj, + include_obj, + exclude_obj, max_obj, &aut_grp_obj, - &data)) { + &data)) { return user_param_obj; } // The clique we are trying to extend is already big enough @@ -633,7 +676,14 @@ Obj FuncDigraphsCliquesFinder(Obj self, Obj args) { } // go! - BronKerbosch(0, 0, limit, &nr_found, max, (size == 0 ? size : size - include_size), aut_grp_obj, &data); + BronKerbosch(0, + 0, + limit, + &nr_found, + max, + (size == 0 ? size : size - include_size), + aut_grp_obj, + &data); return user_param_obj; } From 39a028bddacbcd1ac2bab6607e7d4744b7dd37fd Mon Sep 17 00:00:00 2001 From: Julius Date: Tue, 26 May 2020 14:46:01 +0200 Subject: [PATCH 27/30] more formatting --- src/digraphs.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/digraphs.c b/src/digraphs.c index 66bf05af9..cd67a6e1c 100644 --- a/src/digraphs.c +++ b/src/digraphs.c @@ -19,9 +19,9 @@ #include // for uint64_t #include // for NULL, free +#include "cliques.h" #include "digraphs-debug.h" // for DIGRAPHS_ASSERT #include "homos.h" // for FuncHomomorphismDigraphsFinder -#include "cliques.h" #include "planar.h" // for FUNC_IS_PLANAR, . . . #ifdef DIGRAPHS_WITH_INCLUDED_BLISS From 94c2dbd7fc72cee3f8f3a490fc1be40b6e28718e Mon Sep 17 00:00:00 2001 From: Julius Date: Tue, 26 May 2020 16:59:00 +0200 Subject: [PATCH 28/30] add: improved code coverage for DigraphsCliqueFinder --- gap/cliques.gi | 17 +++++---- src/cliques.c | 4 +-- tst/standard/cliques.tst | 76 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 86 insertions(+), 11 deletions(-) diff --git a/gap/cliques.gi b/gap/cliques.gi index e567e2723..0607ba3e5 100644 --- a/gap/cliques.gi +++ b/gap/cliques.gi @@ -541,8 +541,7 @@ function(arg) return CliquesFinder(D, fail, [], limit, include, exclude, true, size, false); end); - -# A wrapper for DigraphsCliquesFinder +# A wrapper for DigraphsCliquesFinder # This is very hacky at the moment, so we could test C code with GAP tests InstallGlobalFunction(CliquesFinder, function(digraph, hook, user_param, limit, include, exclude, max, size, reps) @@ -642,7 +641,7 @@ function(digraph, hook, user_param, limit, include, exclude, max, size, reps) i := Intersection(x, o); if not IsSubset(x, o) then UniteBlist(exclude_variant, BlistList(vertices, i)); - else + else Append(exclude_invariant, i); fi; x := Difference(x, i); @@ -655,10 +654,10 @@ function(digraph, hook, user_param, limit, include, exclude, max, size, reps) "5th arguments and must be ", "invariant under the action of the automorphism group of ", "the maximal symmetric subdigraph without loops,"); - fi; + fi; fi; - if DigraphNrVertices(digraph) < 512 then + if DigraphNrVertices(digraph) < 512 then if reps then # Might want to pass the group here out := DigraphsCliquesFinder(subgraph, @@ -670,7 +669,7 @@ function(digraph, hook, user_param, limit, include, exclude, max, size, reps) max, size); return MakeImmutable(out); - else + else # Function to find the valid cliques of an orbit given an orbit rep found_orbits := []; @@ -689,9 +688,9 @@ function(digraph, hook, user_param, limit, include, exclude, max, size, reps) Add(found_orbits, orbit); n := Length(orbit); - if invariant_include and invariant_exclude then - # we're not just looking for orbit reps, but inc and exc are invariant - # so there is nothing extra to check + if invariant_include and invariant_exclude then + # we're not just looking for orbit reps, but inc and exc are + # invariant so there is nothing extra to check new_found := Minimum(limit - num_found, n); for clique in orbit{[1 .. new_found]} do hook(usr_param, clique); diff --git a/src/cliques.c b/src/cliques.c index 9b40df3a2..f9d3d8d90 100644 --- a/src/cliques.c +++ b/src/cliques.c @@ -605,7 +605,7 @@ Obj FuncDigraphsCliquesFinder(Obj self, Obj args) { CALL_2ARGS(OnTuples, include_obj, ELM_LIST(gens, i))) != True) { ErrorQuit("the 5th argument must be invaraint under , " - "or the full automorphism if is not given", + "or the full automorphism if is not given,", 0L, 0L); } @@ -615,7 +615,7 @@ Obj FuncDigraphsCliquesFinder(Obj self, Obj args) { CALL_2ARGS(OnTuples, exclude_obj, ELM_LIST(gens, i))) != True) { ErrorQuit("the 6th argument must be invaraint under , " - "or the full automorphism if is not given", + "or the full automorphism if is not given,", 0L, 0L); } diff --git a/tst/standard/cliques.tst b/tst/standard/cliques.tst index 83e82f6a3..df0a93226 100644 --- a/tst/standard/cliques.tst +++ b/tst/standard/cliques.tst @@ -506,6 +506,82 @@ gap> cliques := DigraphMaximalCliques(D, [1]); gap> IsMutable(cliques) or ForAny(cliques, IsMutable); false +# Test DigraphsCliqueFinder +gap> DigraphsCliquesFinder(0); +Error, there must be 8 or 9 arguments, found 1, +gap> DigraphsCliquesFinder(1,2,3,4,5,6,7,8); +Error, the 1st argument must be a digraph, not integer, +gap> DigraphsCliquesFinder(NullDigraph(513),2,3,4,5,6,7,8); +Error, the 1st argument must have at most 512 vertices, found 513, +gap> DigraphsCliquesFinder(NullDigraph(1),fail,3,4,5,6,7,8); +Error, the 2rd argument is fail and so the 3th argument must be a mutab\ +le list, not integer, +gap> f := function(n) end;; +gap> DigraphsCliquesFinder(NullDigraph(1),f,[],4,5,6,7,8); +Error, the 2rd argument must be a function with 2 arguments, +gap> DigraphsCliquesFinder(NullDigraph(1),fail,[],[],5,6,7,8); +Error, the 4th argument must be an integer or infinity, not empty plai\ +n list, +gap> DigraphsCliquesFinder(NullDigraph(1),fail,[],0,5,6,7,8); +Error, the 4th argument must be a positive integer, not 0, +gap> DigraphsCliquesFinder(NullDigraph(1),fail,[],4,5,6,7,8); +Error, the 5th argument must be a list or fail, not integer, +gap> DigraphsCliquesFinder(NullDigraph(1),fail,[],4,[,1],6,7,8); +Error, the 5th argument must be a dense list, +gap> DigraphsCliquesFinder(NullDigraph(1),fail,[],4,[0],6,7,8); +Error, the 5th argument must only contain positive small integers, b\ +ut found integer in position 1, +gap> DigraphsCliquesFinder(NullDigraph(1),fail,[],4,[2],6,7,8); +Error, in the 5th argument position 1 is out of range, must be in th\ +e range [1, 1], +gap> DigraphsCliquesFinder(NullDigraph(1),fail,[],4,[1,1],6,7,8); +Error, in the 5th argument position 2 is a duplicate, +gap> DigraphsCliquesFinder(NullDigraph(1),fail,[],4,[],6,7,8); +Error, the 6th argument must be a list or fail, not integer, +gap> DigraphsCliquesFinder(NullDigraph(1),fail,[],4,[],[,1],7,8); +Error, the 6th argument must be a dense list, +gap> DigraphsCliquesFinder(NullDigraph(1),fail,[],4,[],[0],7,8); +Error, the 6th argument must only contain positive small integers, b\ +ut found integer in position 1, +gap> DigraphsCliquesFinder(NullDigraph(1),fail,[],4,[],[2],7,8); +Error, in the 6th argument position 1 is out of range, must be in th\ +e range [1, 1], +gap> DigraphsCliquesFinder(NullDigraph(1),fail,[],4,[],[1,1],7,8); +Error, in the 6th argument position 2 is a duplicate, +gap> DigraphsCliquesFinder(NullDigraph(1),fail,[],4,[],[],7,8); +Error, the 7th argument must true or false, not integer, +gap> DigraphsCliquesFinder(NullDigraph(1),fail,[],4,[],[],true,0); +Error, the 8th argument must be a positive small integer or fail, not i\ +nteger, +gap> DigraphsCliquesFinder(NullDigraph(1),fail,[],4,[],[],true,fail, 0); +Error, the 9th argument must be a permutation group or fail, not int\ +eger, +gap> DigraphsCliquesFinder(NullDigraph(3),fail,[],4,[],[],true,fail, +> Group((1, 2, 3), (1, 2), (1, 3))); +Error, expected at most 2 generators in the 9th argument but got 3, +gap> DigraphsCliquesFinder(NullDigraph(2),fail,[],4,[],[],true,fail, SymmetricGroup(3)); +Error, expected group of automorphisms, but found a non-automorphism in positi\ +on 1 of the group generators, +gap> DigraphsCliquesFinder(NullDigraph(2),fail,[],4,[1],[],true,fail); +Error, the 5th argument must be invaraint under , or the fu\ +ll automorphism if is not given, +gap> DigraphsCliquesFinder(NullDigraph(2),fail,[],4,[],[1],true,fail); +Error, the 6th argument must be invaraint under , or the fu\ +ll automorphism if is not given, +gap> f := function(a, b) return []; end;; +gap> DigraphsCliquesFinder(CompleteDigraph(2),f,[],4,[],[],true,fail); +Error, the 2rd argument must be a function which returns an integer, +gap> DigraphsCliquesFinder(CompleteDigraph(2),fail,[],4,[1,2],[],true,fail); +[ [ 1, 2 ] ] +gap> DigraphsCliquesFinder(CompleteDigraph(2),fail,[],4,[],[1,2],true,fail); +[ ] +gap> DigraphsCliquesFinder(CompleteDigraph(2),fail,[],4,[],[],true,3); +[ ] +gap> DigraphsCliquesFinder(NullDigraph(2),fail,[],4,[1,2],[],true,fail); +[ ] +gap> DigraphsCliquesFinder(CompleteDigraph(2),fail,[],4,[1,2],[],true,2); +[ [ 1, 2 ] ] + # DIGRAPHS_UnbindVariables gap> Unbind(f); gap> Unbind(c); From 3547812d3ae6e9a1f5380dff52be321c738bc8ed Mon Sep 17 00:00:00 2001 From: Julius Date: Wed, 27 May 2020 11:59:04 +0200 Subject: [PATCH 29/30] Even more linting and code coverage --- doc/cliques.xml | 6 +-- gap/cliques.gi | 13 ++++- tst/standard/cliques.tst | 101 ++++++++++++++++++++++++++------------- 3 files changed, 80 insertions(+), 40 deletions(-) diff --git a/doc/cliques.xml b/doc/cliques.xml index 583bca84f..eac68d714 100644 --- a/doc/cliques.xml +++ b/doc/cliques.xml @@ -552,9 +552,7 @@ gap> DigraphIndependentSets(D, [], [4, 5], 1, 2); If hook is a function, then it should have two arguments user_param (see below) and a clique c. The function hook(user_param, c) is called every time a new - clique c is found by CliquesFinder and should return a - number of cliques obtained from c (for example, by applying - automorphisms of G).

+ clique c is found by CliquesFinder.

If hook is fail, then a default function is used that simply adds every new clique found by CliquesFinder to @@ -636,7 +634,6 @@ gap> D := CompleteDigraph(5); gap> user_param := [];; gap> f := function(a, b) # Calculate size of clique > AddSet(user_param, Size(b)); -> return 1; > end;; gap> CliquesFinder(D, f, user_param, infinity, [], [], false, fail, > true); @@ -656,7 +653,6 @@ gap> D := CompleteDigraph(IsMutableDigraph, 5); gap> user_param := [];; gap> f := function(a, b) # Calculate size of clique > AddSet(user_param, Size(b)); -> return 1; > end;; gap> CliquesFinder(D, f, user_param, infinity, [], [], false, fail, > true); diff --git a/gap/cliques.gi b/gap/cliques.gi index 0607ba3e5..46db38a40 100644 --- a/gap/cliques.gi +++ b/gap/cliques.gi @@ -659,9 +659,18 @@ function(digraph, hook, user_param, limit, include, exclude, max, size, reps) if DigraphNrVertices(digraph) < 512 then if reps then - # Might want to pass the group here + + if hook = fail then + hook_wrapper := fail; + else + hook_wrapper := function(usr_param, clique) + hook(usr_param, clique); + return 1; + end; + fi; + out := DigraphsCliquesFinder(subgraph, - hook, + hook_wrapper, user_param, limit, include, diff --git a/tst/standard/cliques.tst b/tst/standard/cliques.tst index df0a93226..510cbbcf0 100644 --- a/tst/standard/cliques.tst +++ b/tst/standard/cliques.tst @@ -506,81 +506,116 @@ gap> cliques := DigraphMaximalCliques(D, [1]); gap> IsMutable(cliques) or ForAny(cliques, IsMutable); false +# Test CliquesFinder on graphs with more than 512 vertices +gap> CliquesFinder(NullDigraph(513), fail, [], infinity, [], [], true, fail, true); +[ [ 1 ] ] +gap> gr := DigraphSymmetricClosure(ChainDigraph(513)); + +gap> CliquesFinder(gr, fail, [], 4, [], [], true, fail, false); +[ [ 1, 2 ], [ 512, 513 ], [ 2, 3 ], [ 511, 512 ] ] +gap> CliquesFinder(gr, fail, [], 4, [1], [], true, fail, false); +[ [ 1, 2 ] ] +gap> CliquesFinder(gr, fail, [], 4, [], [1], true, fail, false); +[ [ 512, 513 ], [ 2, 3 ], [ 511, 512 ], [ 3, 4 ] ] +gap> CliquesFinder(gr, fail, [], 4, [1], [2], true, fail, false); +[ ] +gap> CliquesFinder(gr, fail, [], 4, [1], [3], true, fail, false); +[ [ 1, 2 ] ] +gap> CliquesFinder(gr, fail, [], 4, [1, 2], [], false, 1, false); +[ ] +gap> CliquesFinder(gr, fail, [], 4, [1, 513], [], false, 1, false); +[ ] +gap> CliquesFinder(gr, fail, [], 4, [1, 2, 513], [], false, 1, false); +[ ] +gap> CliquesFinder(gr, fail, [], 4, [1], [1], false, fail, false); +[ ] +gap> CliquesFinder(gr, fail, [], 4, [], [1 .. 512], false, 2, false); +[ ] +gap> CliquesFinder(gr, fail, [], 4, [1, 3], [], false, 2, false); +[ ] +gap> f := function(a, b) +> Add(a, Size(b)); +> end;; +gap> CliquesFinder(gr, f, [], 4, [], [], false, 2, true); +[ 2, 2, 2, 2 ] + # Test DigraphsCliqueFinder gap> DigraphsCliquesFinder(0); Error, there must be 8 or 9 arguments, found 1, -gap> DigraphsCliquesFinder(1,2,3,4,5,6,7,8); +gap> DigraphsCliquesFinder(1, 2, 3, 4, 5, 6, 7, 8); Error, the 1st argument must be a digraph, not integer, -gap> DigraphsCliquesFinder(NullDigraph(513),2,3,4,5,6,7,8); +gap> DigraphsCliquesFinder(NullDigraph(513), 2, 3, 4, 5, 6, 7, 8); Error, the 1st argument must have at most 512 vertices, found 513, -gap> DigraphsCliquesFinder(NullDigraph(1),fail,3,4,5,6,7,8); +gap> DigraphsCliquesFinder(NullDigraph(1), fail, 3, 4, 5, 6, 7, 8); Error, the 2rd argument is fail and so the 3th argument must be a mutab\ le list, not integer, gap> f := function(n) end;; -gap> DigraphsCliquesFinder(NullDigraph(1),f,[],4,5,6,7,8); +gap> DigraphsCliquesFinder(NullDigraph(1), f, [], 4, 5, 6, 7, 8); Error, the 2rd argument must be a function with 2 arguments, -gap> DigraphsCliquesFinder(NullDigraph(1),fail,[],[],5,6,7,8); -Error, the 4th argument must be an integer or infinity, not empty plai\ -n list, -gap> DigraphsCliquesFinder(NullDigraph(1),fail,[],0,5,6,7,8); +gap> DigraphsCliquesFinder(NullDigraph(1), fail, [], fail, 5, 6, 7, 8); +Error, the 4th argument must be an integer or infinity, not boolean or\ + fail, +gap> DigraphsCliquesFinder(NullDigraph(1), fail, [], 0, 5, 6, 7, 8); Error, the 4th argument must be a positive integer, not 0, -gap> DigraphsCliquesFinder(NullDigraph(1),fail,[],4,5,6,7,8); +gap> DigraphsCliquesFinder(NullDigraph(1), fail, [], 4, 5, 6, 7, 8); Error, the 5th argument must be a list or fail, not integer, -gap> DigraphsCliquesFinder(NullDigraph(1),fail,[],4,[,1],6,7,8); +gap> DigraphsCliquesFinder(NullDigraph(1), fail, [], 4, [, 1], 6, 7, 8); Error, the 5th argument must be a dense list, -gap> DigraphsCliquesFinder(NullDigraph(1),fail,[],4,[0],6,7,8); +gap> DigraphsCliquesFinder(NullDigraph(1), fail, [], 4, [0], 6, 7, 8); Error, the 5th argument must only contain positive small integers, b\ ut found integer in position 1, -gap> DigraphsCliquesFinder(NullDigraph(1),fail,[],4,[2],6,7,8); +gap> DigraphsCliquesFinder(NullDigraph(1), fail, [], 4, [2], 6, 7, 8); Error, in the 5th argument position 1 is out of range, must be in th\ e range [1, 1], -gap> DigraphsCliquesFinder(NullDigraph(1),fail,[],4,[1,1],6,7,8); +gap> DigraphsCliquesFinder(NullDigraph(1), fail, [], 4, [1, 1], 6, 7, 8); Error, in the 5th argument position 2 is a duplicate, -gap> DigraphsCliquesFinder(NullDigraph(1),fail,[],4,[],6,7,8); +gap> DigraphsCliquesFinder(NullDigraph(1), fail, [], 4, [], 6, 7, 8); Error, the 6th argument must be a list or fail, not integer, -gap> DigraphsCliquesFinder(NullDigraph(1),fail,[],4,[],[,1],7,8); +gap> DigraphsCliquesFinder(NullDigraph(1), fail, [], 4, [], [, 1], 7, 8); Error, the 6th argument must be a dense list, -gap> DigraphsCliquesFinder(NullDigraph(1),fail,[],4,[],[0],7,8); +gap> DigraphsCliquesFinder(NullDigraph(1), fail, [], 4, [], [0], 7, 8); Error, the 6th argument must only contain positive small integers, b\ ut found integer in position 1, -gap> DigraphsCliquesFinder(NullDigraph(1),fail,[],4,[],[2],7,8); +gap> DigraphsCliquesFinder(NullDigraph(1), fail, [], 4, [], [2], 7, 8); Error, in the 6th argument position 1 is out of range, must be in th\ e range [1, 1], -gap> DigraphsCliquesFinder(NullDigraph(1),fail,[],4,[],[1,1],7,8); +gap> DigraphsCliquesFinder(NullDigraph(1), fail, [], 4, [], [1, 1], 7, 8); Error, in the 6th argument position 2 is a duplicate, -gap> DigraphsCliquesFinder(NullDigraph(1),fail,[],4,[],[],7,8); +gap> DigraphsCliquesFinder(NullDigraph(1), fail, [], 4, [], [], 7, 8); Error, the 7th argument must true or false, not integer, -gap> DigraphsCliquesFinder(NullDigraph(1),fail,[],4,[],[],true,0); +gap> DigraphsCliquesFinder(NullDigraph(1), fail, [], 4, [], [], true, 0); Error, the 8th argument must be a positive small integer or fail, not i\ nteger, -gap> DigraphsCliquesFinder(NullDigraph(1),fail,[],4,[],[],true,fail, 0); +gap> DigraphsCliquesFinder(NullDigraph(1), fail, [], 4, [], [], true, fail, 0); Error, the 9th argument must be a permutation group or fail, not int\ eger, -gap> DigraphsCliquesFinder(NullDigraph(3),fail,[],4,[],[],true,fail, +gap> DigraphsCliquesFinder(NullDigraph(3), fail, [], 4, [], [], true, fail, > Group((1, 2, 3), (1, 2), (1, 3))); Error, expected at most 2 generators in the 9th argument but got 3, -gap> DigraphsCliquesFinder(NullDigraph(2),fail,[],4,[],[],true,fail, SymmetricGroup(3)); +gap> DigraphsCliquesFinder(NullDigraph(2), fail, [], 4, [], [], true, fail, SymmetricGroup(3)); Error, expected group of automorphisms, but found a non-automorphism in positi\ on 1 of the group generators, -gap> DigraphsCliquesFinder(NullDigraph(2),fail,[],4,[1],[],true,fail); +gap> DigraphsCliquesFinder(NullDigraph(2), fail, [], 4, [1], [], true, fail); Error, the 5th argument must be invaraint under , or the fu\ ll automorphism if is not given, -gap> DigraphsCliquesFinder(NullDigraph(2),fail,[],4,[],[1],true,fail); +gap> DigraphsCliquesFinder(NullDigraph(2), fail, [], 4, [], [1], true, fail); Error, the 6th argument must be invaraint under , or the fu\ ll automorphism if is not given, -gap> f := function(a, b) return []; end;; -gap> DigraphsCliquesFinder(CompleteDigraph(2),f,[],4,[],[],true,fail); -Error, the 2rd argument must be a function which returns an integer, -gap> DigraphsCliquesFinder(CompleteDigraph(2),fail,[],4,[1,2],[],true,fail); +gap> DigraphsCliquesFinder(CompleteDigraph(2), fail, [], 4, [1, 2], [], true, fail); [ [ 1, 2 ] ] -gap> DigraphsCliquesFinder(CompleteDigraph(2),fail,[],4,[],[1,2],true,fail); +gap> DigraphsCliquesFinder(CompleteDigraph(2), fail, [], 4, [], [1, 2], true, fail); [ ] -gap> DigraphsCliquesFinder(CompleteDigraph(2),fail,[],4,[],[],true,3); +gap> DigraphsCliquesFinder(CompleteDigraph(2), fail, [], 4, [], [], true, 3); [ ] -gap> DigraphsCliquesFinder(NullDigraph(2),fail,[],4,[1,2],[],true,fail); +gap> DigraphsCliquesFinder(NullDigraph(2), fail, [], 4, [1, 2], [], true, fail); [ ] -gap> DigraphsCliquesFinder(CompleteDigraph(2),fail,[],4,[1,2],[],true,2); +gap> DigraphsCliquesFinder(CompleteDigraph(2), fail, [], 4, [1, 2], [], true, 2); [ [ 1, 2 ] ] +gap> f := function(a, b) +> Add(a, Size(b)); +> end;; +gap> CliquesFinder(CompleteDigraph(3), f, [], 4, [], [], false, 2, true); +[ 2 ] # DIGRAPHS_UnbindVariables gap> Unbind(f); From 4f73cb9e04598311d742cdcea63f64547e1db63f Mon Sep 17 00:00:00 2001 From: Finn Smith Date: Wed, 19 Aug 2020 17:39:15 +0100 Subject: [PATCH 30/30] fix memory leaks and assertion failure --- src/cliques.c | 55 +++++++++++++++++++++++++++++++++------------------ 1 file changed, 36 insertions(+), 19 deletions(-) diff --git a/src/cliques.c b/src/cliques.c index f9d3d8d90..3a90169e1 100644 --- a/src/cliques.c +++ b/src/cliques.c @@ -106,13 +106,25 @@ static UInt clique_hook_collect(void* user_param, return 1; } +static UInt clique_hook_gap_list(void* user_param, + Obj clique_list, + Obj gap_func) { + Obj n = CALL_2ARGS(gap_func, user_param, clique_list); + if (!IS_INTOBJ(n)) { + ErrorQuit("the 2rd argument must be a function which returns " + "an integer,", + 0L, + 0L); + } + return INT_INTOBJ(n); +} + static UInt clique_hook_gap(void* user_param, const BitArray* clique, const uint16_t nr, Obj gap_func) { UInt i; Obj c; - Obj n; c = NEW_PLIST(T_PLIST, nr); for (i = 1; i <= nr; ++i) { @@ -120,15 +132,7 @@ static UInt clique_hook_gap(void* user_param, PushPlist(c, INTOBJ_INT(i)); } } - - n = CALL_2ARGS(gap_func, user_param, c); - if (!IS_INTOBJ(n)) { - ErrorQuit("the 2rd argument must be a function which returns " - "an integer,", - 0L, - 0L); - } - return INT_INTOBJ(n); + return clique_hook_gap_list(user_param, c, gap_func); } //////////////////////////////////////////////////////////////////////////////// @@ -239,37 +243,40 @@ static bool init_data_from_args(Obj digraph_obj, } // Update TRY using exclude_obj if (exclude_obj != Fail) { - BitArray* exclude = new_bit_array(MAXVERTS); - set_bit_array_from_gap_list(exclude, exclude_obj); - complement_bit_arrays(get_conditions(data->try, 0), exclude, nr); + set_bit_array_from_gap_list(data->temp_bitarray, exclude_obj); + complement_bit_arrays( + get_conditions(data->try, 0), data->temp_bitarray, nr); } // Get the isolated vertices of the graph - BitArray* isolated = new_bit_array(MAXVERTS); - Int first_isolated = -1; + // temp_bitarray now represents isolated vertices + init_bit_array(data->temp_bitarray, false, nr); + Int first_isolated = -1; for (uint16_t i = 0; i < nr; ++i) { if (size_bit_array(data->graph->neighbours[i], nr) == 0) { if (first_isolated == -1 && get_bit_array(get_conditions(data->try, 0), i)) { first_isolated = i; } - set_bit_array(isolated, i, true); + set_bit_array(data->temp_bitarray, i, true); } } // Update TRY using isolated, only one isolated vertex is used if (first_isolated != -1) { - complement_bit_arrays(get_conditions(data->try, 0), isolated, nr); + complement_bit_arrays( + get_conditions(data->try, 0), data->temp_bitarray, nr); set_bit_array(get_conditions(data->try, 0), first_isolated, true); } // Discard the generators of aut_grp_obj which act on the isolated vertices - if (size_bit_array(isolated, nr) > 0) { + if (size_bit_array(data->temp_bitarray, nr) > 0) { Obj new_group = Fail; Obj gens = CALL_1ARGS(GeneratorsOfGroup, *group); DIGRAPHS_ASSERT(IS_LIST(gens)); for (Int i = 1; i <= LEN_LIST(gens); ++i) { Obj s = CALL_1ARGS(SmallestMovedPointPerm, ELM_LIST(gens, i)); - if (s != Infinity && !get_bit_array(isolated, INT_INTOBJ(s) - 1)) { + if (s != Infinity + && !get_bit_array(data->temp_bitarray, INT_INTOBJ(s) - 1)) { if (new_group == Fail) { new_group = CALL_1ARGS(Group, ELM_LIST(gens, i)); } else { @@ -627,6 +634,16 @@ Obj FuncDigraphsCliquesFinder(Obj self, Obj args) { uint16_t nr = DigraphNrVertices(digraph_obj); // Check the trivial cases: + // The digraph has 0 vertices + if (nr == 0) { + Obj c = NEW_PLIST(T_PLIST, 0); + if (hook_obj != Fail) { + clique_hook_gap_list(user_param_obj, c, hook_obj); + } else { + ASS_LIST(user_param_obj, LEN_LIST(user_param_obj) + 1, c); + } + return user_param_obj; + } // The desired clique is too small if (size != 0 && include_size > size) { return user_param_obj;