// // homos.c di/graph homomorphisms Julius Jonusas // J. D. Mitchell // // Copyright (C) 2014-19 - Julius Jonusas and J. D. Mitchell // // This file is free software, see the digraphs/LICENSE.
//////////////////////////////////////////////////////////////////////////////// // // This file is organised as follows: // // 1. Macros // 2. Forward declarations // 3. Global variables // 4. Hook functions // 5. Static helper functions // 6. The main recursive functions (and helpers) // 7. The GAP-level function (and helpers) // ////////////////////////////////////////////////////////////////////////////////
// TODO(later) // 0. remove final arg from push_conditions // 1. Try other bit hacks for iterating through set bits
#include"homos.h"
// C headers #include <setjmp.h> // for longjmp, setjmp, jmp_buf #include <stdbool.h> // for true, false, bool #include <stddef.h> // for NULL #include <stdint.h> // for uint16_t, uint64_t #include <stdlib.h> // for malloc, NULL
#ifdef DIGRAPHS_ENABLE_STATS #include <cstdio> // for printf #endif
// GAP headers #include"gap-includes.h"
// Digraphs package headers #include"bitarray.h"// for BitArray #include"bliss-includes.h"// for bliss stuff #include"conditions.h"// for Conditions #include"digraphs-config.h"// for DIGRAPHS_HAVE___BUILTIN_CTZLL #include"digraphs-debug.h"// for DIGRAPHS_ASSERT #include"globals.h"// for UNDEFINED... #include"homos-graphs.h"// for Digraph, Graph, . . . #include"perms.h"// for UNDEFINED, PermColl, Perm #include"safemalloc.h"// for safe_mallov #include"schreier-sims.h"// for PermColl, . . .
#ifdef DIGRAPHS_ENABLE_STATS // This include has to come after digraphs-config.h since that's where // DIGRAPHS_ENABLE_STATS is defined #include <time.h> // for time #endif
// The next line can be used instead of the first line of STORE_MIN to // randomise which vertex of minimum degree is used next, but I didn't find any // cases where this made things faster.
//////////////////////////////////////////////////////////////////////////////// // 3. 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 uint16_t*); staticvoid* USER_PARAM; // a USER_PARAM for the hook
static jmp_buf OUTOFHERE; // so we can jump out of the deepest
staticbool ORDERED; // true if the vertices of the domain/source digraph // should be considered in a different order than they are // given, false otherwise.
static BitArray** BIT_ARRAY_BUFFER = NULL; // A buffer static BitArray* IMAGE_RESTRICT; // Values in MAP must be in this static BitArray** MAP_UNDEFINED = NULL; // UNDEFINED positions in MAP static BitArray* ORB_LOOKUP; // points in orbit static BitArray* VALS; // Values in MAP already
static BitArray** REPS; // orbit reps organised by depth
static Conditions* CONDITIONS;
static Digraph* DIGRAPH1; // Digraphs to hold incoming GAP digraphs static Digraph* DIGRAPH2;
static Graph* GRAPH1; // Graphs to hold incoming GAP symmetric digraphs static Graph* GRAPH2;
static BlissGraph** BLISS_GRAPH = NULL;
static uint16_t* MAP = NULL; // partial image list static uint16_t* COLORS2 = NULL; // colors of range (di)graph static uint16_t* INVERSE_ORDER = NULL; // external -> internal static uint16_t* MAP_BUFFER = NULL; // For converting from internal -> // external and back when calling the // hook functions. static uint16_t* ORB = NULL; // Array for containing nodes in an orbit. static uint16_t* ORDER = NULL; // internal -> external
bool homos_data_initialized = false; // did we call this method before?
Obj FuncDIGRAPHS_FREE_HOMOS_DATA(Obj self) { if (homos_data_initialized) {
free_digraph(DIGRAPH1);
free_digraph(DIGRAPH2);
free_graph(GRAPH1);
free_graph(GRAPH2);
free_bit_array(IMAGE_RESTRICT);
free_bit_array(ORB_LOOKUP);
free(MAP);
free(COLORS2);
free(INVERSE_ORDER);
free(MAP_BUFFER);
free(ORB);
free(ORDER);
for (uint16_t i = 0; i < HOMOS_STRUCTURE_SIZE * 3; i++) {
bliss_digraphs_release(BLISS_GRAPH[i]);
}
for (uint16_t i = 0; i < HOMOS_STRUCTURE_SIZE; i++) {
free_bit_array(REPS[i]);
free_bit_array(BIT_ARRAY_BUFFER[i]);
free_bit_array(MAP_UNDEFINED[i]);
free_perm_coll(STAB_GENS[i]);
}
if (!reorder) { 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);
}
}
} else {
DIGRAPHS_ASSERT(ORDERED); for (uint16_t i = 1; i <= nr; i++) { // Nodes in the new graph
Obj nbs = ELM_PLIST(out, ORDER[i - 1] + 1);
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, INVERSE_ORDER[INT_INTOBJ(ELM_LIST(nbs, j)) - 1]);
}
}
}
}
// Find orbit representatives of the group generated by STAB_GENS[rep_depth] // and store them in REPS[rep_depth], only values in IMAGE_RESTRICT will be // chosen as orbit representatives. staticbool compute_stabs_and_orbit_reps(uint16_t const nr_nodes_1,
uint16_t const nr_nodes_2,
uint16_t const rep_depth,
uint16_t const depth,
uint16_t const pt, boolconst first_call) {
DIGRAPHS_ASSERT(rep_depth <= depth + 1); if (depth == nr_nodes_1 - 1 && !first_call) { // first_call is required in the case that nr_nodes_1 is 1, since without // first_call this function returns false (in the next line) and REPS is // not initialised. returnfalse; // This doesn't really say anything about the stabiliser
} elseif (rep_depth > 0) {
point_stabilizer(
SCHREIER_SIMS, STAB_GENS[rep_depth - 1], STAB_GENS[rep_depth], pt); if (STAB_GENS[rep_depth]->size == 0) { // the stabiliser of pt in STAB_GENS[rep_depth - 1] is trivial
copy_bit_array(REPS[rep_depth], IMAGE_RESTRICT, nr_nodes_2);
complement_bit_arrays(REPS[rep_depth], VALS, nr_nodes_2); // REPS[rep_depth] is a set of orbit representatives of the stabiliser of // the existing values in MAP, which belong to VALS, and since the // stabiliser is trivial, we take valid choice as the set of // representatives. returntrue; // the stabiliser is trivial
}
}
init_bit_array(REPS[rep_depth], false, nr_nodes_2);
copy_bit_array(ORB_LOOKUP, VALS, nr_nodes_2);
uint16_t fst = 0; while (fst < PERM_DEGREE
&& (get_bit_array(ORB_LOOKUP, fst)
|| !get_bit_array(IMAGE_RESTRICT, fst))) {
fst++;
}
while (fst < PERM_DEGREE) {
ORB[0] = fst;
uint16_t n = 1; // length of ORB
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);
}
}
} while (fst < PERM_DEGREE
&& (get_bit_array(ORB_LOOKUP, fst)
|| !get_bit_array(IMAGE_RESTRICT, fst))) {
fst++;
}
} returnfalse; // the stabiliser is not trivial
}
// Rewrite the global variable MAP so that it contains a homomorphism of the // original GAP level (di)graph, and not the possibly distinct (but isomorphic) // copy in the homomorphism search. This should be called before any calls to // the hook functions (i.e. after an entire homomorphism is found). staticvoid external_order_map_digraph(Digraph* digraph) { if (!ORDERED) { return;
} for (uint16_t i = 0; i < digraph->nr_vertices; ++i) {
MAP_BUFFER[ORDER[i]] = MAP[i];
} for (uint16_t i = 0; i < digraph->nr_vertices; ++i) {
MAP[i] = MAP_BUFFER[i];
}
}
staticvoid external_order_map_graph(Graph* graph) { if (!ORDERED) { return;
} for (uint16_t i = 0; i < graph->nr_vertices; ++i) {
MAP_BUFFER[ORDER[i]] = MAP[i];
} for (uint16_t i = 0; i < graph->nr_vertices; ++i) {
MAP[i] = MAP_BUFFER[i];
}
}
// Rewrite the global variable MAP so that it contains a homomorphism of the // internal (di)graph, and not the possibly distinct (but isomorphic) GAP level // (di)graph. This should be called after any calls to the hook functions // (i.e. after an entire homomorphism is found). staticvoid internal_order_map_digraph(Digraph const* const digraph) { if (!ORDERED) { return;
} for (uint16_t i = 0; i < digraph->nr_vertices; ++i) {
MAP_BUFFER[INVERSE_ORDER[i]] = MAP[i];
} for (uint16_t i = 0; i < digraph->nr_vertices; ++i) {
MAP[i] = MAP_BUFFER[i];
}
}
staticvoid internal_order_map_graph(Graph const* const graph) { if (!ORDERED) { return;
} for (uint16_t i = 0; i < graph->nr_vertices; ++i) {
MAP_BUFFER[INVERSE_ORDER[i]] = MAP[i];
} for (uint16_t i = 0; i < graph->nr_vertices; ++i) {
MAP[i] = MAP_BUFFER[i];
}
}
//////////////////////////////////////////////////////////////////////////////// // 6. The main recursive functions (and helpers) ////////////////////////////////////////////////////////////////////////////////
// The main recursive function for homomorphisms of graphs. // // The arguments are: // // 1. depth The current depth of the search (i.e. number of // positions in MAP that are assigned). // 2. pos The last position in MAP that was filled // 3. rep_depth The depth of the stabilisers of points in MAP already. // This is different than depth because we stop increasing // the rep_depth when we first encounter a trivial // stabiliser. // 4. has_trivial_stab true if the stabiliser of the values already in MAP is // trivial, false otherwise. // 5. rank The current number of distinct values in MAP. // 6. max_results The maximum number of results to find. // 7. hint The desired number of distinct points in a (full) // homomorphism. // 8. count The number of homomorphisms found so far. staticvoid find_graph_homos(uint16_t depth,
uint16_t pos,
uint16_t rep_depth, bool has_trivial_stab,
uint16_t rank,
uint64_t const max_results,
uint64_t const hint,
uint64_t* const count) { #ifdef DIGRAPHS_ENABLE_STATS
update_stats(STATS, depth); #endif if (depth == GRAPH1->nr_vertices) { // Every position in MAP is assigned . . . if (hint != UNDEFINED && rank != hint) { #ifdef DIGRAPHS_ENABLE_STATS
STATS->nr_dead_branches++; #endif return;
}
external_order_map_graph(GRAPH1);
Obj ret =
HOOK(USER_PARAM, MAX(GRAPH1->nr_vertices, GRAPH2->nr_vertices), MAP);
internal_order_map_graph(GRAPH1);
(*count)++; if (*count >= max_results || ret == True) {
longjmp(OUTOFHERE, 1);
} return;
}
uint16_t next = 0; // the next position to fill
uint16_t min = UNDEFINED; // the minimum number of candidates for MAP[next]
uint16_t i;
BitArray* possible = BIT_ARRAY_BUFFER[depth];
if (depth > 0) { // this is not the first call of the function
copy_bit_array(
MAP_UNDEFINED[depth], MAP_UNDEFINED[depth - 1], GRAPH1->nr_vertices);
copy_bit_array(possible, MAP_UNDEFINED[depth], GRAPH1->nr_vertices);
intersect_bit_arrays(
possible, GRAPH1->neighbours[pos], GRAPH1->nr_vertices);
FOR_SET_BITS(possible, GRAPH1->nr_vertices, i) {
size_t const n = graph_homo_update_conditions(depth, pos, i); if (n == 0) { #ifdef DIGRAPHS_ENABLE_STATS
STATS->nr_dead_branches++; #endif
pop_conditions(CONDITIONS, depth); return;
}
STORE_MIN(min, next, n, i);
}
END_FOR_SET_BITS if (min > 1) {
copy_bit_array(possible, MAP_UNDEFINED[depth], GRAPH1->nr_vertices);
complement_bit_arrays(
possible, GRAPH1->neighbours[pos], GRAPH1->nr_vertices);
FOR_SET_BITS(possible, GRAPH1->nr_vertices, i) {
size_t const n = size_conditions(CONDITIONS, i);
STORE_MIN_BREAK(min, next, n, i);
}
END_FOR_SET_BITS
}
} else { for (i = 0; i < GRAPH1->nr_vertices; ++i) {
size_t const n = size_conditions(CONDITIONS, i);
STORE_MIN_BREAK(min, next, n, i);
}
}
DIGRAPHS_ASSERT(get_bit_array(MAP_UNDEFINED[depth], next));
DIGRAPHS_ASSERT(next < GRAPH1->nr_vertices);
// The main recursive function for monomorphisms of graphs. // // The arguments are: // // 1. depth The current depth of the search (i.e. number of // positions in MAP that are assigned). // 2. pos The last position in MAP that was filled // 3. rep_depth The depth of the stabilisers of points in MAP already. // This is different than depth because we stop increasing // the rep_depth when we first encounter a trivial // stabiliser. // 4. has_trivial_stab true if the stabiliser of the values already in MAP is // trivial, false otherwise. // 5. max_results The maximum number of results to find. // 6. count The number of embeddings found so far. staticvoid find_graph_monos(uint16_t depth,
uint16_t pos,
uint16_t rep_depth, bool has_trivial_stab,
uint64_t const max_results,
uint64_t* const count) { #ifdef DIGRAPHS_ENABLE_STATS
update_stats(STATS, depth); #endif if (depth == GRAPH1->nr_vertices) { // we've assigned every position in <MAP>
external_order_map_graph(GRAPH1);
Obj ret =
HOOK(USER_PARAM, MAX(GRAPH1->nr_vertices, GRAPH2->nr_vertices), MAP);
internal_order_map_graph(GRAPH1);
(*count)++; if (*count >= max_results || ret == True) {
longjmp(OUTOFHERE, 1);
} return;
}
uint16_t next = 0; // the next position to fill
uint16_t min = UNDEFINED; // the minimum number of candidates for MAP[next]
uint16_t i;
BitArray* possible = BIT_ARRAY_BUFFER[depth];
if (depth > 0) { // this is not the first call of the function
copy_bit_array(
MAP_UNDEFINED[depth], MAP_UNDEFINED[depth - 1], GRAPH1->nr_vertices);
copy_bit_array(possible, MAP_UNDEFINED[depth], GRAPH1->nr_vertices);
intersect_bit_arrays(
possible, GRAPH1->neighbours[pos], GRAPH1->nr_vertices);
FOR_SET_BITS(possible, GRAPH1->nr_vertices, i) {
size_t const n = graph_mono_update_conditions(depth, pos, i); if (n == 0) { #ifdef DIGRAPHS_ENABLE_STATS
STATS->nr_dead_branches++; #endif
pop_conditions(CONDITIONS, depth); return;
}
STORE_MIN(min, next, n, i);
}
END_FOR_SET_BITS if (min > 1) {
copy_bit_array(possible, MAP_UNDEFINED[depth], GRAPH1->nr_vertices);
complement_bit_arrays(
possible, GRAPH1->neighbours[pos], GRAPH1->nr_vertices);
FOR_SET_BITS(possible, GRAPH1->nr_vertices, i) {
size_t const n = size_conditions(CONDITIONS, i);
STORE_MIN_BREAK(min, next, n, i);
}
END_FOR_SET_BITS
}
} else { for (i = 0; i < GRAPH1->nr_vertices; ++i) {
size_t const n = size_conditions(CONDITIONS, i);
STORE_MIN_BREAK(min, next, n, i);
}
}
copy_bit_array(
possible, get_conditions(CONDITIONS, next), GRAPH2->nr_vertices);
intersect_bit_arrays(possible, REPS[rep_depth], GRAPH2->nr_vertices);
complement_bit_arrays(possible, VALS, GRAPH2->nr_vertices);
FOR_SET_BITS(possible, GRAPH2->nr_vertices, i) {
MAP[next] = i;
set_bit_array(VALS, i, true);
set_bit_array(MAP_UNDEFINED[depth], next, false); if (!has_trivial_stab) {
find_graph_monos(depth + 1,
next,
rep_depth + 1,
compute_stabs_and_orbit_reps(GRAPH1->nr_vertices,
GRAPH2->nr_vertices,
rep_depth + 1,
depth,
i, false),
max_results,
count);
} else {
find_graph_monos(depth + 1, next, rep_depth, true, max_results, count);
}
MAP[next] = UNDEFINED;
set_bit_array(VALS, i, false);
set_bit_array(MAP_UNDEFINED[depth], next, true);
}
END_FOR_SET_BITS
pop_conditions(CONDITIONS, depth);
}
// The main recursive function for embeddings of graphs. // // The arguments are: // // 1. depth The current depth of the search (i.e. number of // positions in MAP that are assigned). // 2. pos The last position in MAP that was filled // 3. rep_depth The depth of the stabilisers of points in MAP already. // This is different than depth because we stop increasing // the rep_depth when we first encounter a trivial // stabiliser. // 4. has_trivial_stab true if the stabiliser of the values already in MAP is // trivial, false otherwise. // 5. max_results The maximum number of results to find. // 6. count The number of embeddings found so far. staticvoid find_graph_embeddings(uint16_t depth,
uint16_t pos,
uint16_t rep_depth, bool has_trivial_stab,
uint64_t const max_results,
uint64_t* const count) { #ifdef DIGRAPHS_ENABLE_STATS
update_stats(STATS, depth); #endif if (depth == GRAPH1->nr_vertices) { // we've assigned every position in <MAP>
external_order_map_graph(GRAPH1);
Obj ret =
HOOK(USER_PARAM, MAX(GRAPH1->nr_vertices, GRAPH2->nr_vertices), MAP);
internal_order_map_graph(GRAPH1);
(*count)++; if (*count >= max_results || ret == True) {
longjmp(OUTOFHERE, 1);
} return;
}
uint16_t next = 0; // the next position to fill
uint16_t min = UNDEFINED; // the minimum number of candidates for MAP[next]
uint16_t i;
BitArray* possible = BIT_ARRAY_BUFFER[depth];
if (depth > 0) { // this is not the first call of the function
copy_bit_array(
MAP_UNDEFINED[depth], MAP_UNDEFINED[depth - 1], GRAPH1->nr_vertices);
copy_bit_array(possible, MAP_UNDEFINED[depth], GRAPH1->nr_vertices);
intersect_bit_arrays(
possible, GRAPH1->neighbours[pos], GRAPH1->nr_vertices);
FOR_SET_BITS(possible, GRAPH1->nr_vertices, i) {
size_t const n =
graph_embed_update_conditions(depth, pos, i, &intersect_bit_arrays); if (n == 0) { #ifdef DIGRAPHS_ENABLE_STATS
STATS->nr_dead_branches++; #endif
pop_conditions(CONDITIONS, depth); return;
}
STORE_MIN(min, next, n, i);
}
END_FOR_SET_BITS
copy_bit_array(possible, MAP_UNDEFINED[depth], GRAPH1->nr_vertices);
complement_bit_arrays(
possible, GRAPH1->neighbours[pos], GRAPH1->nr_vertices);
FOR_SET_BITS(possible, GRAPH1->nr_vertices, i) {
size_t const n =
graph_embed_update_conditions(depth, pos, i, &complement_bit_arrays); if (n == 0) { #ifdef DIGRAPHS_ENABLE_STATS
STATS->nr_dead_branches++; #endif
pop_conditions(CONDITIONS, depth); return;
}
STORE_MIN(min, next, n, i);
}
END_FOR_SET_BITS
} else { for (i = 0; i < GRAPH1->nr_vertices; ++i) {
size_t const n = size_conditions(CONDITIONS, i);
STORE_MIN_BREAK(min, next, n, i);
}
}
//////////////////////////////////////////////////////////////////////////////// // The next function should be called before find_graph_homos. This function // simulates the first steps of the recursion using the information in // partial_map_obj (if any) so that the values specified in partial_map_obj are // installed in MAP first. ////////////////////////////////////////////////////////////////////////////////
// The main recursive function for homomorphisms of digraphs. // // The arguments are: // // 1. depth The current depth of the search (i.e. number of // positions in MAP that are assigned). // 2. pos The last position in MAP that was filled // 3. rep_depth The depth of the stabilisers of points in MAP already. // This is different than depth because we stop increasing // the rep_depth when we first encounter a trivial // stabiliser. // 4. has_trivial_stab true if the stabiliser of the values already in MAP is // trivial, false otherwise. // 5. rank The current number of distinct values in MAP. // 6. max_results The maximum number of results to find. // 7. hint The desired number of distinct points in a (full) // homomorphism. // 8. count The number of homomorphisms found so far. staticvoid find_digraph_homos(uint16_t depth,
uint16_t pos,
uint16_t rep_depth, bool has_trivial_stab,
uint16_t rank,
uint64_t const max_results,
uint64_t const hint,
uint64_t* const count) { #ifdef DIGRAPHS_ENABLE_STATS
update_stats(STATS, depth); #endif if (depth == DIGRAPH1->nr_vertices) { // we've assigned every position in <MAP> if (hint != UNDEFINED && rank != hint) { #ifdef DIGRAPHS_ENABLE_STATS
STATS->nr_dead_branches++; #endif return;
}
external_order_map_digraph(DIGRAPH1);
Obj ret = HOOK(
USER_PARAM, MAX(DIGRAPH1->nr_vertices, DIGRAPH2->nr_vertices), MAP);
internal_order_map_digraph(DIGRAPH1);
(*count)++; if (*count >= max_results || ret == True) {
longjmp(OUTOFHERE, 1);
} return;
}
uint16_t next = 0; // the next position to fill
uint16_t min = UNDEFINED; // the minimum number of candidates for MAP[next]
uint16_t i;
if (depth > 0) { // this is not the first call of the function
copy_bit_array(
MAP_UNDEFINED[depth], MAP_UNDEFINED[depth - 1], DIGRAPH1->nr_vertices);
FOR_SET_BITS(MAP_UNDEFINED[depth], DIGRAPH1->nr_vertices, i) {
uint16_t const n = digraph_homo_update_conditions(depth, pos, i); if (n == 0) { #ifdef DIGRAPHS_ENABLE_STATS
STATS->nr_dead_branches++; #endif
pop_conditions(CONDITIONS, depth); return;
}
STORE_MIN(min, next, n, i);
}
END_FOR_SET_BITS
} else { // depth == 0 for (i = 0; i < DIGRAPH1->nr_vertices; ++i) {
size_t const n = size_conditions(CONDITIONS, i);
STORE_MIN_BREAK(min, next, n, i);
}
}
// Helper for the main recursive monomorphism of digraphs function. static ALWAYS_INLINE uint16_t
digraph_mono_update_conditions(uint16_t const depth,
uint16_t const last_defined,
uint16_t const vertex) {
push_conditions(CONDITIONS, depth, vertex, NULL); if (is_adjacent_digraph(DIGRAPH1, last_defined, vertex)) {
intersect_bit_arrays(get_conditions(CONDITIONS, vertex),
DIGRAPH2->out_neighbours[MAP[last_defined]],
DIGRAPH2->nr_vertices);
} if (is_adjacent_digraph(DIGRAPH1, vertex, last_defined)) {
intersect_bit_arrays(get_conditions(CONDITIONS, vertex),
DIGRAPH2->in_neighbours[MAP[last_defined]],
DIGRAPH2->nr_vertices);
}
store_size_conditions(CONDITIONS, vertex);
return size_conditions(CONDITIONS, vertex);
}
// The main recursive function for monomorphisms of digraphs. // // The arguments are: // // 1. depth The current depth of the search (i.e. number of // positions in MAP that are assigned). // 2. pos The last position in MAP that was filled // 3. rep_depth The depth of the stabilisers of points in MAP already. // This is different than depth because we stop increasing // the rep_depth when we first encounter a trivial // stabiliser. // 4. has_trivial_stab true if the stabiliser of the values already in MAP is // trivial, false otherwise. // 5. max_results The maximum number of results to find. // 6. count The number of embeddings found so far. staticvoid find_digraph_monos(uint16_t depth,
uint16_t pos,
uint16_t rep_depth, bool has_trivial_stab,
uint64_t const max_results,
uint64_t* const count) { #ifdef DIGRAPHS_ENABLE_STATS
update_stats(STATS, depth); #endif if (depth == DIGRAPH1->nr_vertices) { // we've assigned every position in <MAP>
external_order_map_digraph(DIGRAPH1);
Obj ret = HOOK(
USER_PARAM, MAX(DIGRAPH1->nr_vertices, DIGRAPH2->nr_vertices), MAP);
internal_order_map_digraph(DIGRAPH1);
(*count)++; if (*count >= max_results || ret == True) {
longjmp(OUTOFHERE, 1);
} return;
}
uint16_t next = 0; // the next position to fill
uint16_t min = UNDEFINED; // the minimum number of candidates for MAP[next]
uint16_t i;
if (depth > 0) { // this is not the first call of the function
copy_bit_array(
MAP_UNDEFINED[depth], MAP_UNDEFINED[depth - 1], DIGRAPH1->nr_vertices);
FOR_SET_BITS(MAP_UNDEFINED[depth], DIGRAPH1->nr_vertices, i) {
size_t const n = digraph_mono_update_conditions(depth, pos, i); if (n == 0) { #ifdef DIGRAPHS_ENABLE_STATS
STATS->nr_dead_branches++; #endif
pop_conditions(CONDITIONS, depth); return;
}
STORE_MIN(min, next, n, i);
}
END_FOR_SET_BITS
} else { for (i = 0; i < DIGRAPH1->nr_vertices; i++) {
size_t const n = size_conditions(CONDITIONS, i);
STORE_MIN_BREAK(min, next, n, i);
}
}
// The main recursive function for embeddings of digraphs. // // The arguments are: // // 1. depth The current depth of the search (i.e. number of // positions in MAP that are assigned). // 2. pos The last position in MAP that was filled // 3. rep_depth The depth of the stabilisers of points in MAP already. // This is different than depth because we stop increasing // the rep_depth when we first encounter a trivial // stabiliser. // 4. has_trivial_stab true if the stabiliser of the values already in MAP is // trivial, false otherwise. // 5. max_results The maximum number of results to find. // 6. count The number of embeddings found so far. staticvoid find_digraph_embeddings(uint16_t depth,
uint16_t pos,
uint16_t rep_depth, bool has_trivial_stab,
uint64_t const max_results,
uint64_t* const count) { #ifdef DIGRAPHS_ENABLE_STATS
update_stats(STATS, depth); #endif if (depth == DIGRAPH1->nr_vertices) { // we've assigned every position in <MAP>
external_order_map_digraph(DIGRAPH1);
Obj ret = HOOK(
USER_PARAM, MAX(DIGRAPH1->nr_vertices, DIGRAPH2->nr_vertices), MAP);
internal_order_map_digraph(DIGRAPH1);
(*count)++; if (*count >= max_results || ret == True) {
longjmp(OUTOFHERE, 1);
} return;
}
uint16_t next = 0; // the next position to fill
uint16_t min = UNDEFINED; // the minimum number of candidates for MAP[next]
uint16_t i;
if (depth > 0) { // this is not the first call of the function
copy_bit_array(
MAP_UNDEFINED[depth], MAP_UNDEFINED[depth - 1], DIGRAPH1->nr_vertices);
FOR_SET_BITS(MAP_UNDEFINED[depth], DIGRAPH1->nr_vertices, i) {
size_t const n = digraph_embed_update_conditions(depth, pos, i); if (n == 0) { #ifdef DIGRAPHS_ENABLE_STATS
STATS->nr_dead_branches++; #endif
pop_conditions(CONDITIONS, depth); return;
}
STORE_MIN(min, next, n, i);
}
END_FOR_SET_BITS
} else { for (i = 0; i < DIGRAPH1->nr_vertices; i++) {
size_t const n = size_conditions(CONDITIONS, i);
STORE_MIN_BREAK(min, next, n, i);
}
}
//////////////////////////////////////////////////////////////////////////////// // The next function should be called before find_digraph_homos. This function // simulates the first steps of the recursion using the information in // partial_map_obj (if any) so that the values specified in partial_map_obj are // installed in MAP first. ////////////////////////////////////////////////////////////////////////////////
if (partial_map_obj != Fail) { for (next = 0; next < LEN_LIST(partial_map_obj); ++next) { if (ISB_LIST(partial_map_obj, next + 1)) { if (depth > 0) {
DIGRAPHS_ASSERT(last_defined != UNDEFINED);
copy_bit_array(MAP_UNDEFINED[depth],
MAP_UNDEFINED[depth - 1],
DIGRAPH1->nr_vertices);
uint16_t i; // variable for the next for-loop
FOR_SET_BITS(MAP_UNDEFINED[depth], DIGRAPH1->nr_vertices, i) {
uint16_t n; if (INT_INTOBJ(injective_obj) == 0) {
n = digraph_homo_update_conditions(depth, last_defined, i);
} elseif (INT_INTOBJ(injective_obj) == 1) {
n = digraph_mono_update_conditions(depth, last_defined, i);
} else {
DIGRAPHS_ASSERT(INT_INTOBJ(injective_obj) == 2);
n = digraph_embed_update_conditions(depth, last_defined, i);
} if (n == 0) { return;
}
}
END_FOR_SET_BITS
}
uint16_t val = INT_INTOBJ(ELM_LIST(partial_map_obj, next + 1)) - 1;
next = (ORDERED ? INVERSE_ORDER[next] : next);
MAP[next] = val; if (!get_bit_array(VALS, MAP[next])) {
rank++; if (rank > hint) { return;
}
}
set_bit_array(VALS, MAP[next], true);
set_bit_array(MAP_UNDEFINED[depth], next, false); if (!last_stab_is_trivial) {
last_stab_is_trivial =
compute_stabs_and_orbit_reps(DIGRAPH1->nr_vertices,
DIGRAPH2->nr_vertices,
rep_depth + 1,
depth,
MAP[next], false);
rep_depth++;
}
depth++;
last_defined = next;
next = (ORDERED ? ORDER[next] : next);
}
}
} if (INT_INTOBJ(injective_obj) == 0) {
find_digraph_homos(depth,
last_defined,
rep_depth,
last_stab_is_trivial,
rank,
max_results,
hint,
count);
} elseif (INT_INTOBJ(injective_obj) == 1) {
find_digraph_monos(depth,
last_defined,
rep_depth,
last_stab_is_trivial,
max_results,
count);
} else {
DIGRAPHS_ASSERT(INT_INTOBJ(injective_obj) == 2);
find_digraph_embeddings(depth,
last_defined,
rep_depth,
last_stab_is_trivial,
max_results,
count);
} return;
}
//////////////////////////////////////////////////////////////////////////////// // 7. The GAP-level function (and helpers) ////////////////////////////////////////////////////////////////////////////////
// Initialises the data structures required by the recursive functions for // finding homomorphisms. If true is returned everything was initialised ok, if // false is returned, then the arguments already imply that there can be no // homomorphisms. staticbool init_data_from_args(Obj digraph1_obj,
Obj digraph2_obj,
Obj hook_obj,
Obj user_param_obj,
Obj max_results_obj,
Obj hint_obj,
Obj injective_obj,
Obj image_obj,
Obj partial_map_obj,
Obj colors1_obj,
Obj colors2_obj,
Obj order_obj,
Obj aut_grp_obj) {
uint16_t calculated_max_verts =
MAX(DigraphNrVertices(digraph1_obj), DigraphNrVertices(digraph2_obj)); if (!homos_data_initialized
|| (calculated_max_verts >= HOMOS_STRUCTURE_SIZE)) {
FuncDIGRAPHS_FREE_HOMOS_DATA(0L);
homos_data_initialized = true;
// Rather arbitrary, but we multiply by 1.2 to avoid // n = 1,2,3,4,5... causing constant reallocation
HOMOS_STRUCTURE_SIZE =
(calculated_max_verts + calculated_max_verts / 5) + 1; // The previous line includes "+ 1" because below we do: // "BLISS_GRAPH[3 * PERM_DEGREE]" but we only allocate bliss graphs in // BLISS_GRAPH up to but not including 3 * HOMOS_STRUCTURE_SIZE. So if // PERM_DEGREE = (# nodes in digraph2) = HOMOS_STRUCTURE_SIZE (the // first equality always holds, the second if # nodes in digraph2 > // # nodes in digraph1), then this is out of bounds. #ifdef DIGRAPHS_ENABLE_STATS
STATS = safe_malloc(sizeof(HomoStats)); #endif
DIGRAPH1 = new_digraph(HOMOS_STRUCTURE_SIZE);
DIGRAPH2 = new_digraph(HOMOS_STRUCTURE_SIZE);
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung ist noch experimentell.