Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactor Floyd–Warshall C implementation #657

Merged
merged 1 commit into from
Jun 19, 2024
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
189 changes: 98 additions & 91 deletions src/digraphs.c
Original file line number Diff line number Diff line change
Expand Up @@ -1115,78 +1115,66 @@ static Obj FuncIS_MULTI_DIGRAPH(Obj self, Obj digraph) {
* - 3 integers i, j, k
* - An integer n (the number of vertices of digraph)
* and modifies the matrix dist according to the values of i, j, k.
* 3. Int val1 s.t initially dist[i][j] = val1 if [ i, j ] isn't an edge.
* 4. Int val2 s.t initially dist[i][j] = val2 if [ i, j ] is an edge.
* 5. bool copy:
* - If true, FLOYD_WARSHALL stores the initialised dist, and
* compares it with dist after it has gone through the 3 for-loops,
* and returns true iff it is unchanged.
* - If false, proceeds as usual Floyd-Warshall algorithm and returns
* a GAP object matrix as the result.
* 6. bool diameter: // TODO wouldn't it be better to just take a
* "post-processing" function. JDM
* - If true, FLOYD_WARSHALL goes through dist after the 3 for-loops,
* returns -1 if dist contains the value -1, else it returns the
* maximum value of dist
* - If false, proceeds as usual
* 7. bool shortest:
* 3. Int nonEdgeVal s.t initially dist[i][j] = nonEdgeVal if [ i, j ] isn't
* an edge.
* 4. Int edgeVal s.t initially dist[i][j] = edgeVal if [ i, j ] is an edge.
* 5. bool keepCopy:
* - If true, FLOYD_WARSHALL stores a copy of the initialised dist, which
* can later be used by the post function
* 6. bool zeroSelfDist:
* - If true, for each vertex i, dist[i][i] is initially set to 0
* - If false, this step is skipped
* 7. A special function which takes 3 arguments:
* - The matrix dist
* - The matrix distCopy, which is a copy of dist from before func was run,
* and is only set if keepCopy is true
* - An integer n (the number of vertices of digraph)
* and returns the object that will be the output of this function.
*/
static Obj FLOYD_WARSHALL(Obj digraph,
void (*func)(Int** dist, Int i, Int j, Int k, Int n),
Int val1,
Int val2,
bool copy,
bool diameter,
bool shortest) {
Int nonEdgeVal,
Int edgeVal,
bool keepCopy,
bool zeroSelfDist,
Obj (*post)(Int** dist, Int** distCopy, Int n)) {
Int n, i, j, k, *dist;
Int* adj = NULL;
Obj next, out, outi, val;
Int* distCopy = NULL;
Obj out, neighbours, neighboursi;

n = DigraphNrVertices(digraph);

// Special case for 0-vertex graph
if (n == 0) {
if (diameter) {
return Fail;
}
if (copy) {
return True;
}
return NEW_PLIST_IMM(T_PLIST_EMPTY, 0);
}

// Initialise the n x n matrix with val1 and val2
// Initialise the n x n matrix with nonEdgeVal and edgeVal
dist = safe_malloc(n * n * sizeof(Int));
for (i = 0; i < n * n; i++) {
dist[i] = val1;
dist[i] = nonEdgeVal;
}
out = FuncOutNeighbours(0L, digraph);
neighbours = FuncOutNeighbours(0L, digraph);
for (i = 1; i <= n; i++) {
outi = ELM_PLIST(out, i);
PLAIN_LIST(outi);
for (j = 1; j <= LEN_PLIST(outi); j++) {
k = (i - 1) * n + INT_INTOBJ(ELM_PLIST(outi, j)) - 1;
dist[k] = val2;
neighboursi = ELM_PLIST(neighbours, i);
PLAIN_LIST(neighboursi);
for (j = 1; j <= LEN_PLIST(neighboursi); j++) {
k = (i - 1) * n + INT_INTOBJ(ELM_PLIST(neighboursi, j)) - 1;
dist[k] = edgeVal;
}
}

if (shortest) {
if (zeroSelfDist) {
// This is the special case for DIGRAPH_SHORTEST_DIST
for (i = 0; i < n; i++) {
dist[i * n + i] = 0;
}
}

if (copy) {
if (keepCopy) {
// This is the special case for IS_TRANSITIVE_DIGRAPH
adj = safe_malloc(n * n * sizeof(Int));
distCopy = safe_malloc(n * n * sizeof(Int));
for (i = 0; i < n * n; i++) {
adj[i] = dist[i];
distCopy[i] = dist[i];
}
}

// Main loop
for (k = 0; k < n; k++) {
for (i = 0; i < n; i++) {
for (j = 0; j < n; j++) {
Expand All @@ -1195,48 +1183,44 @@ static Obj FLOYD_WARSHALL(Obj digraph,
}
}

// the following is a horrible hack
if (diameter) {
// This is the special case for DIGRAPH_DIAMETER
Int maximum = -1;
for (i = 0; i < n; i++) {
for (j = 0; j < n; j++) {
if (dist[i * n + j] > maximum) {
maximum = dist[i * n + j];
} else if (dist[i * n + j] == -1) {
free(dist);
free(adj);
return Fail;
}
}
}
free(dist);
return INTOBJ_INT(maximum);
// Apply post-processing function
out = post(&dist, &distCopy, n);

free(dist);
if (keepCopy) {
free(distCopy);
}

if (copy) {
// This is the special case for IS_TRANSITIVE_DIGRAPH
for (i = 0; i < n * n; i++) {
if (adj[i] != dist[i]) {
free(dist);
free(adj);
return False;
}
return out;
}

static void FW_FUNC_SHORTEST_DIST(Int** dist, Int i, Int j, Int k, Int n) {
if ((*dist)[i * n + k] != -1 && (*dist)[k * n + j] != -1) {
if ((*dist)[i * n + j] == -1
|| (*dist)[i * n + j] > (*dist)[i * n + k] + (*dist)[k * n + j]) {
(*dist)[i * n + j] = (*dist)[i * n + k] + (*dist)[k * n + j];
}
free(dist);
free(adj);
return True;
}
}

static Obj FW_POST_DISTANCES(Int** dist, Int** distCopy, Int n) {
// Main Floyd-Warshall use case: return GAP matrix of distances
Obj next, out, val;
Int i, j;

// Special case for 0-vertex graph
if (n == 0) {
return NEW_PLIST_IMM(T_PLIST_EMPTY, 0);
}

// Create GAP matrix to return
out = NEW_PLIST(T_PLIST_TAB, n);
SET_LEN_PLIST(out, n);

for (i = 1; i <= n; i++) {
next = NEW_PLIST(T_PLIST_CYC, n);
SET_LEN_PLIST(next, n);
for (j = 1; j <= n; j++) {
val = INTOBJ_INT(dist[(i - 1) * n + (j - 1)]);
val = INTOBJ_INT((*dist)[(i - 1) * n + (j - 1)]);
if (val == INTOBJ_INT(-1)) {
val = Fail;
}
Expand All @@ -1246,28 +1230,38 @@ static Obj FLOYD_WARSHALL(Obj digraph,
CHANGED_BAG(out);
}
SET_FILT_LIST(out, FN_IS_RECT);

free(dist);
return out;
}

static void FW_FUNC_SHORTEST_DIST(Int** dist, Int i, Int j, Int k, Int n) {
if ((*dist)[i * n + k] != -1 && (*dist)[k * n + j] != -1) {
if ((*dist)[i * n + j] == -1
|| (*dist)[i * n + j] > (*dist)[i * n + k] + (*dist)[k * n + j]) {
(*dist)[i * n + j] = (*dist)[i * n + k] + (*dist)[k * n + j];
}
}
}

static Obj FuncDIGRAPH_SHORTEST_DIST(Obj self, Obj digraph) {
return FLOYD_WARSHALL(
digraph, FW_FUNC_SHORTEST_DIST, -1, 1, false, false, true);
digraph, FW_FUNC_SHORTEST_DIST, -1, 1, false, true, FW_POST_DISTANCES);
}

static Obj FW_POST_DIAMETER(Int** dist, Int** distCopy, Int n) {
// Return the diameter of the digraph
Int i, j;
Int maximum = -1;

if (n == 0) {
return Fail;
}

for (i = 0; i < n; i++) {
for (j = 0; j < n; j++) {
if ((*dist)[i * n + j] > maximum) {
maximum = (*dist)[i * n + j];
} else if ((*dist)[i * n + j] == -1) {
return Fail;
}
}
}
return INTOBJ_INT(maximum);
}

static Obj FuncDIGRAPH_DIAMETER(Obj self, Obj digraph) {
return FLOYD_WARSHALL(
digraph, FW_FUNC_SHORTEST_DIST, -1, 1, false, true, true);
digraph, FW_FUNC_SHORTEST_DIST, -1, 1, false, true, FW_POST_DIAMETER);
}

static void FW_FUNC_TRANS_CLOSURE(Int** dist, Int i, Int j, Int k, Int n) {
Expand All @@ -1276,14 +1270,26 @@ static void FW_FUNC_TRANS_CLOSURE(Int** dist, Int i, Int j, Int k, Int n) {
}
}

static Obj FW_POST_HAS_CHANGED(Int** dist, Int** distCopy, Int n) {
// Determines whether dist and distCopy are the same
// Used for IS_TRANSITIVE_DIGRAPH
Int i;
for (i = 0; i < n * n; i++) {
if ((*distCopy)[i] != (*dist)[i]) {
return False;
}
}
return True;
}

static Obj FuncIS_TRANSITIVE_DIGRAPH(Obj self, Obj digraph) {
return FLOYD_WARSHALL(
digraph, FW_FUNC_TRANS_CLOSURE, 0, 1, true, false, false);
digraph, FW_FUNC_TRANS_CLOSURE, 0, 1, true, false, FW_POST_HAS_CHANGED);
}

static Obj FuncDIGRAPH_TRANS_CLOSURE(Obj self, Obj digraph) {
return FLOYD_WARSHALL(
digraph, FW_FUNC_TRANS_CLOSURE, 0, 1, false, false, false);
digraph, FW_FUNC_TRANS_CLOSURE, 0, 1, false, false, FW_POST_DISTANCES);
}

static void
Expand All @@ -1295,7 +1301,8 @@ FW_FUNC_REFLEX_TRANS_CLOSURE(Int** dist, Int i, Int j, Int k, Int n) {

static Obj FuncDIGRAPH_REFLEX_TRANS_CLOSURE(Obj self, Obj digraph) {
return FLOYD_WARSHALL(
digraph, FW_FUNC_REFLEX_TRANS_CLOSURE, 0, 1, false, false, false);
digraph, FW_FUNC_REFLEX_TRANS_CLOSURE,
0, 1, false, false, FW_POST_DISTANCES);
}

static Obj FuncRANDOM_DIGRAPH(Obj self, Obj nn, Obj limm) {
Expand Down
Loading