You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
526 lines
18 KiB
526 lines
18 KiB
/*
|
|
* Copyright(C) 1999-2020, 2023 National Technology & Engineering Solutions
|
|
* of Sandia, LLC (NTESS). Under the terms of Contract DE-NA0003525 with
|
|
* NTESS, the U.S. Government retains certain rights in this software.
|
|
*
|
|
* See packages/seacas/LICENSE for details
|
|
*/
|
|
|
|
#include "defs.h"
|
|
#include "smalloc.h"
|
|
#include <stdio.h>
|
|
|
|
/* STATUS:
|
|
film.graph random+KL. before size = 24, should stay 24.
|
|
However, I'm not finding this much flow.
|
|
|
|
Start w/ a simple greedy weighted matching.
|
|
For each node on left side w/ some remaining unmatched weight:
|
|
Look for augmenting path via recursive call.
|
|
If my neighbor has uncommitted weight, take it.
|
|
Otherwise, if some committed elsewhere, see who it is committed to.
|
|
If this second order neighbor can get flow elsewhere, patch
|
|
up augmenting path.
|
|
|
|
When no further improvement possible:
|
|
For all unsatisfied nodes on left, include right nodes
|
|
in their search tree, and exclude left nodes.
|
|
Include all other left nodes.
|
|
|
|
This is similar to matching except:
|
|
Each flow edge has a value associated with it.
|
|
Vertex can have several flow neighbors.
|
|
|
|
These differences require a different data structure.
|
|
I need to modify the flow associated w/ an edge and see it
|
|
immediately from either vertex. Use a single representation
|
|
of flow on an edge, and have each edge representation point to it.
|
|
|
|
*/
|
|
|
|
/*
|
|
The following code takes a weighted bipartite graph as input (with
|
|
n_left+n_right nodes, where node 'i' is adjacent to nodes
|
|
'indices[pointers[i]]' through 'indices[pointers[i+1]-1]', all
|
|
0-based) and returns a minimum weight edge cover (in sep_nodes[]).
|
|
|
|
*/
|
|
|
|
static void bpflow(int n_left, /* number of vertices on left side */
|
|
int n_right, /* number of vertices on right side */
|
|
int *pointers, /* start/stop of adjacency lists */
|
|
int *indices, /* adjacency list for each vertex */
|
|
int *vweight, /* vertex weights */
|
|
int *resid, /* residual weight at each vertex */
|
|
int *flow, /* flow on right->left edges */
|
|
int *touched /* flags for each vertex */
|
|
);
|
|
static void touch(int node, int *pointers, /* start/stop of adjacency lists */
|
|
int *indices, /* adjacency list for each vertex */
|
|
int *resid, /* residual weight at each vertex */
|
|
int *flow, /* flow on right->left edges */
|
|
int *touched, /* flags for each vertex */
|
|
int *flow1, /* max flow we are looking for */
|
|
int *seen, /* list of vertices encountered */
|
|
int *nseen /* number of vertices encountered */
|
|
);
|
|
static void reachability(int n_left, /* number of vertices on left side */
|
|
int n_right, /* number of vertices on right side */
|
|
int *pointers, /* start/stop of adjacency lists */
|
|
int *indices, /* adjacency list for each vertex */
|
|
int *resid, /* residual weight at each vertex */
|
|
int *flow, /* flow on right->left edges */
|
|
int *touched /* flags for each vertex */
|
|
);
|
|
static int touch2(int node, int *pointers, /* start/stop of adjacency lists */
|
|
int *indices, /* adjacency list for each vertex */
|
|
int *flow, /* flow on right->left edges */
|
|
int *touched /* flags for each vertex */
|
|
);
|
|
static int augment(int node, /* start node in augmenting path */
|
|
int *pointers, /* start/stop of adjacency lists */
|
|
int *indices, /* adjacency list for each vertex */
|
|
int *resid, /* residual weight at each vertex */
|
|
int *flow, /* flow on right->left edges */
|
|
int *touched, /* flags for each vertex */
|
|
int *seen /* keeps list of vertices encountered */
|
|
);
|
|
|
|
void wbpcover(int n_left, /* number of vertices on left side */
|
|
int n_right, /* number of vertices on right side */
|
|
int *pointers, /* start/stop of adjacency lists */
|
|
int *indices, /* adjacency list for each vertex */
|
|
int *vweight, /* vertex weights */
|
|
int *psep_size, /* returned size of separator */
|
|
int *psep_weight, /* returned weight of separator */
|
|
int **psep_nodes /* list of separator nodes */
|
|
)
|
|
{
|
|
extern int DEBUG_COVER; /* debug flag for this routine */
|
|
int *touched; /* flags for each vertex */
|
|
int *resid; /* remaining, unmatched vertex weight */
|
|
int *flow; /* flow on each right->left edge */
|
|
int *sep_nodes; /* list of separator nodes */
|
|
int sep_size; /* returned size of separator */
|
|
int sep_weight; /* returned weight of separator */
|
|
int nedges; /* number of edges in bipartite graph */
|
|
int i, j; /* loop counter */
|
|
int wleft, wright, wedges;
|
|
|
|
if (DEBUG_COVER) {
|
|
printf("-> Entering wbpcover, nleft = %d, nright = %d, 2*nedges = %d\n", n_left, n_right,
|
|
pointers[n_left + n_right] - pointers[0]);
|
|
|
|
wleft = wright = 0;
|
|
wedges = 0;
|
|
for (i = 0; i < n_left; i++) {
|
|
wleft += vweight[i];
|
|
for (j = pointers[i]; j < pointers[i + 1]; j++) {
|
|
wedges += vweight[i] * vweight[indices[j]];
|
|
}
|
|
}
|
|
for (i = n_left; i < n_left + n_right; i++) {
|
|
wright += vweight[i];
|
|
for (j = pointers[i]; j < pointers[i + 1]; j++) {
|
|
wedges += vweight[i] * vweight[indices[j]];
|
|
}
|
|
}
|
|
printf(" Corresponds to unweighted, nleft = %d, nright = %d, 2*nedges = %d\n", wleft, wright,
|
|
wedges);
|
|
}
|
|
|
|
nedges = pointers[n_left + n_right] - pointers[0];
|
|
|
|
resid = smalloc((n_left + n_right) * sizeof(int));
|
|
touched = smalloc((n_left + n_right) * sizeof(int));
|
|
flow = smalloc((nedges + 1) * sizeof(int));
|
|
|
|
/* Not a matching. I can be connected to multiple nodes. */
|
|
bpflow(n_left, n_right, pointers, indices, vweight, resid, flow, touched);
|
|
|
|
reachability(n_left, n_right, pointers, indices, resid, flow, touched);
|
|
|
|
/* Separator includes untouched nodes on left, touched on right. */
|
|
/* Left separator nodes if unconnected to unmatched right node via */
|
|
/* augmenting path, right separator nodes otherwise. */
|
|
|
|
/* First count the separator size for malloc. */
|
|
sep_size = 0;
|
|
for (i = 0; i < n_left; i++) {
|
|
if (!touched[i]) {
|
|
sep_size++;
|
|
}
|
|
}
|
|
for (i = n_left; i < n_left + n_right; i++) {
|
|
if (touched[i]) {
|
|
sep_size++;
|
|
}
|
|
}
|
|
|
|
sep_nodes = smalloc((sep_size + 1) * sizeof(int));
|
|
|
|
sep_size = sep_weight = 0;
|
|
for (i = 0; i < n_left; i++) {
|
|
if (!touched[i]) {
|
|
sep_nodes[sep_size++] = i;
|
|
sep_weight += vweight[i];
|
|
}
|
|
}
|
|
for (i = n_left; i < n_left + n_right; i++) {
|
|
if (touched[i]) {
|
|
sep_nodes[sep_size++] = i;
|
|
sep_weight += vweight[i];
|
|
}
|
|
}
|
|
|
|
sep_nodes[sep_size] = 0;
|
|
|
|
*psep_size = sep_size;
|
|
*psep_weight = sep_weight;
|
|
*psep_nodes = sep_nodes;
|
|
|
|
/* Check the answer. */
|
|
if (DEBUG_COVER) {
|
|
confirm_cover(n_left, n_right, pointers, indices, flow, vweight, resid, sep_size, sep_nodes);
|
|
}
|
|
|
|
sfree(flow);
|
|
sfree(touched);
|
|
sfree(resid);
|
|
}
|
|
|
|
static void bpflow(int n_left, /* number of vertices on left side */
|
|
int n_right, /* number of vertices on right side */
|
|
int *pointers, /* start/stop of adjacency lists */
|
|
int *indices, /* adjacency list for each vertex */
|
|
int *vweight, /* vertex weights */
|
|
int *resid, /* residual weight at each vertex */
|
|
int *flow, /* flow on right->left edges */
|
|
int *touched /* flags for each vertex */
|
|
)
|
|
{
|
|
int *seen; /* space for list of encountered vertices */
|
|
int neighbor; /* neighboring vertex */
|
|
int flow1; /* flow through a particular edge */
|
|
int modified; /* was flow enlarged? */
|
|
int i, j; /* loop counters */
|
|
|
|
/* First mark all the vertices as unmatched & untouched. */
|
|
for (i = 0; i < n_left + n_right; i++) {
|
|
resid[i] = vweight[i];
|
|
touched[i] = FALSE;
|
|
}
|
|
|
|
/* Note that I only keep flow in edges from right to left. */
|
|
for (i = pointers[n_left]; i < pointers[n_left + n_right]; i++) {
|
|
flow[i] = 0;
|
|
}
|
|
|
|
/* Now generate a fast, greedy flow to start. */
|
|
for (i = n_left; i < n_left + n_right; i++) {
|
|
for (j = pointers[i]; j < pointers[i + 1] && resid[i]; j++) {
|
|
neighbor = indices[j];
|
|
if (resid[neighbor]) {
|
|
flow1 = min(resid[i], resid[neighbor]);
|
|
resid[neighbor] -= flow1;
|
|
resid[i] -= flow1;
|
|
flow[j] = flow1;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Now try to enlarge flow via augmenting paths. */
|
|
|
|
seen = smalloc((n_left + n_right) * sizeof(int));
|
|
|
|
/* Look for an augmenting path. */
|
|
for (i = 0; i < n_left; i++) {
|
|
modified = TRUE;
|
|
while (resid[i] && modified) {
|
|
modified = augment(i, pointers, indices, resid, flow, touched, seen);
|
|
}
|
|
}
|
|
|
|
sfree(seen);
|
|
}
|
|
|
|
static int augment(int node, /* start node in augmenting path */
|
|
int *pointers, /* start/stop of adjacency lists */
|
|
int *indices, /* adjacency list for each vertex */
|
|
int *resid, /* residual weight at each vertex */
|
|
int *flow, /* flow on right->left edges */
|
|
int *touched, /* flags for each vertex */
|
|
int *seen /* keeps list of vertices encountered */
|
|
)
|
|
{
|
|
int nseen; /* number of vertices encountered */
|
|
int flow1; /* flow redirected via augmenting path */
|
|
int i; /* loop counter */
|
|
|
|
/* Look for augmenting path in graph. */
|
|
|
|
nseen = 0;
|
|
flow1 = resid[node];
|
|
touch(node, pointers, indices, resid, flow, touched, &flow1, seen, &nseen);
|
|
|
|
if (flow1) { /* Found an augmenting path! */
|
|
/* Free all the vertices encountered in search. */
|
|
/* Otherwise they can't be involved in augmentation, */
|
|
/* so leave them touched. */
|
|
for (i = 0; i < nseen; i++) {
|
|
touched[*seen++] = 0;
|
|
}
|
|
return (TRUE);
|
|
}
|
|
|
|
return (FALSE);
|
|
}
|
|
|
|
/* Mark everybody in my alternating path tree, and return vertex at */
|
|
/* end of augmenting path if found. */
|
|
static void touch(int node, int *pointers, /* start/stop of adjacency lists */
|
|
int *indices, /* adjacency list for each vertex */
|
|
int *resid, /* residual weight at each vertex */
|
|
int *flow, /* flow on right->left edges */
|
|
int *touched, /* flags for each vertex */
|
|
int *flow1, /* max flow we are looking for */
|
|
int *seen, /* list of vertices encountered */
|
|
int *nseen /* number of vertices encountered */
|
|
)
|
|
{
|
|
int flow2; /* flow passed down recursion tree */
|
|
int neighbor; /* neighbor of a vertex */
|
|
int i, j, k; /* loop counters */
|
|
|
|
touched[node] = TRUE;
|
|
seen[(*nseen)++] = node;
|
|
|
|
for (i = pointers[node]; i < pointers[node + 1]; i++) {
|
|
neighbor = indices[i];
|
|
if (!touched[neighbor]) { /* Not yet considered. */
|
|
touched[neighbor] = TRUE;
|
|
seen[(*nseen)++] = neighbor;
|
|
if (resid[neighbor]) { /* Has flow to spare. */
|
|
flow2 = min(*flow1, resid[neighbor]);
|
|
|
|
/* Adjust flow & resid values. */
|
|
resid[neighbor] -= flow2;
|
|
resid[node] -= flow2;
|
|
/* Perhaps better to compute these upfront once? */
|
|
for (k = pointers[neighbor]; k < pointers[neighbor + 1] && indices[k] != node; k++) {
|
|
;
|
|
}
|
|
flow[k] += flow2;
|
|
*flow1 = flow2;
|
|
return;
|
|
}
|
|
/* Has no flow to spare. */
|
|
for (j = pointers[neighbor]; j < pointers[neighbor + 1]; j++) {
|
|
if (flow[j] && !touched[indices[j]]) {
|
|
flow2 = min(*flow1, flow[j]);
|
|
touch(indices[j], pointers, indices, resid, flow, touched, &flow2, seen, nseen);
|
|
if (flow2) { /* Found some flow to spare! */
|
|
/* Adjust flow & resid values. */
|
|
resid[indices[j]] += flow2;
|
|
resid[node] -= flow2;
|
|
flow[j] -= flow2;
|
|
for (k = pointers[neighbor]; k < pointers[neighbor + 1] && indices[k] != node; k++) {
|
|
;
|
|
}
|
|
flow[k] += flow2;
|
|
*flow1 = flow2;
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
*flow1 = 0;
|
|
}
|
|
|
|
static void reachability(int n_left, /* number of vertices on left side */
|
|
int n_right, /* number of vertices on right side */
|
|
int *pointers, /* start/stop of adjacency lists */
|
|
int *indices, /* adjacency list for each vertex */
|
|
int *resid, /* residual weight at each vertex */
|
|
int *flow, /* flow on right->left edges */
|
|
int *touched /* flags for each vertex */
|
|
)
|
|
{
|
|
int i; /* loop counter */
|
|
|
|
/* Initialize all the vertices to be untouched */
|
|
for (i = 0; i < n_left + n_right; i++) {
|
|
touched[i] = 0;
|
|
}
|
|
|
|
for (i = 0; i < n_left; i++) {
|
|
if (!touched[i] && resid[i]) {
|
|
touch2(i, pointers, indices, flow, touched);
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Mark everybody in my alternating path tree, and return vertex at */
|
|
/* end of augmenting path if found. */
|
|
static int touch2(int node, int *pointers, /* start/stop of adjacency lists */
|
|
int *indices, /* adjacency list for each vertex */
|
|
int *flow, /* flow on right->left edges */
|
|
int *touched /* flags for each vertex */
|
|
)
|
|
{
|
|
int neighbor; /* neighbor of a vertex */
|
|
int result; /* return value */
|
|
int i, j; /* loop counters */
|
|
|
|
touched[node] = TRUE;
|
|
|
|
for (i = pointers[node]; i < pointers[node + 1]; i++) {
|
|
neighbor = indices[i];
|
|
if (!touched[neighbor]) { /* Not yet considered. */
|
|
touched[neighbor] = TRUE;
|
|
for (j = pointers[neighbor]; j < pointers[neighbor + 1]; j++) {
|
|
if (flow[j] && !touched[indices[j]]) {
|
|
result = touch2(indices[j], pointers, indices, flow, touched);
|
|
if (result) {
|
|
return (1);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return (0);
|
|
}
|
|
|
|
void confirm_cover(int n_left, int n_right, int *pointers, int *indices, int *flow, int *vweight,
|
|
int *resid, /* residual weight at each vertex */
|
|
int sep_size, int *sep_nodes)
|
|
{
|
|
int *marked;
|
|
int sep_weight;
|
|
int i, j, neighbor;
|
|
|
|
marked = smalloc((n_left + n_right) * sizeof(int));
|
|
|
|
for (i = 0; i < n_left + n_right; i++) {
|
|
marked[i] = FALSE;
|
|
}
|
|
|
|
sep_weight = 0;
|
|
for (i = 0; i < sep_size; i++) {
|
|
marked[sep_nodes[i]] = TRUE;
|
|
sep_weight += vweight[sep_nodes[i]];
|
|
}
|
|
|
|
for (i = 0; i < n_left; i++) {
|
|
if (!marked[i]) {
|
|
for (j = pointers[i]; j < pointers[i + 1]; j++) {
|
|
neighbor = indices[j];
|
|
if (!marked[neighbor]) {
|
|
printf("Edge (%d, %d) not covered\n", i, neighbor);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
i = count_flow(n_left, n_right, pointers, flow);
|
|
if (i != sep_weight) {
|
|
printf("ERROR: total_flow = %d, sep_weight = %d, sep_size = %d\n", i, sep_weight, sep_size);
|
|
}
|
|
else {
|
|
printf("total_flow = %d, sep_weight = %d, sep_size = %d\n", i, sep_weight, sep_size);
|
|
}
|
|
|
|
/* Now check if any separator nodes have remaining flow. */
|
|
count_resid(n_left, n_right, resid, vweight, marked);
|
|
|
|
check_resid(n_left, n_right, vweight, resid, pointers, indices, flow);
|
|
|
|
sfree(marked);
|
|
}
|
|
|
|
int count_flow(int n_left, int n_right, int *pointers, int *flow)
|
|
{
|
|
int i, total_flow;
|
|
|
|
total_flow = 0;
|
|
for (i = pointers[n_left]; i < pointers[n_left + n_right]; i++) {
|
|
total_flow += flow[i];
|
|
}
|
|
return (total_flow);
|
|
}
|
|
|
|
void count_resid(int n_left, int n_right, int *resid, int *vweight, int *marked)
|
|
{
|
|
int i, left_used, right_used;
|
|
|
|
for (i = 0; i < n_left + n_right; i++) {
|
|
if (resid[i] < 0 || resid[i] > vweight[i]) {
|
|
printf("BAD resid[%d] = %d, vweight = %d\n", i, resid[i], vweight[i]);
|
|
}
|
|
}
|
|
|
|
left_used = right_used = 0;
|
|
for (i = 0; i < n_left; i++) {
|
|
left_used += vweight[i] - resid[i];
|
|
if (marked[i] && resid[i]) {
|
|
printf("Vertex %d in separator, but resid = %d (vweight = %d)\n", i, resid[i], vweight[i]);
|
|
}
|
|
}
|
|
for (i = n_left; i < n_left + n_right; i++) {
|
|
right_used += vweight[i] - resid[i];
|
|
if (marked[i] && resid[i]) {
|
|
printf("Vertex %d in separator, but resid = %d (vweight = %d)\n", i, resid[i], vweight[i]);
|
|
}
|
|
}
|
|
if (left_used != right_used) {
|
|
printf("left_used = %d, NOT EQUAL TO right_used = %d\n", left_used, right_used);
|
|
}
|
|
}
|
|
|
|
void check_resid(int n_left, int n_right, int *vweight, /* vertex weights */
|
|
int *resid, int *pointers, int *indices, int *flow)
|
|
{
|
|
int i, j, left_used, right_used;
|
|
int *diff;
|
|
|
|
for (i = 0; i < n_left + n_right; i++) {
|
|
if (resid[i] < 0 || resid[i] > vweight[i]) {
|
|
printf("BAD resid[%d] = %d, vweight = %d\n", i, resid[i], vweight[i]);
|
|
}
|
|
}
|
|
|
|
left_used = right_used = 0;
|
|
for (i = 0; i < n_left; i++) {
|
|
left_used += vweight[i] - resid[i];
|
|
}
|
|
for (i = n_left; i < n_left + n_right; i++) {
|
|
right_used += vweight[i] - resid[i];
|
|
}
|
|
if (left_used != right_used) {
|
|
printf("left_used = %d, NOT EQUAL TO right_resid = %d\n", left_used, right_used);
|
|
}
|
|
|
|
diff = smalloc((n_left + n_right) * sizeof(int));
|
|
for (i = 0; i < n_left + n_right; i++) {
|
|
diff[i] = 0;
|
|
}
|
|
|
|
for (i = n_left; i < n_left + n_right; i++) {
|
|
for (j = pointers[i]; j < pointers[i + 1]; j++) {
|
|
if (flow[j] < 0) {
|
|
printf("Negative flow (%d,%d) = %d\n", i, indices[j], flow[j]);
|
|
}
|
|
diff[i] += flow[j];
|
|
diff[indices[j]] += flow[j];
|
|
}
|
|
}
|
|
for (i = 0; i < n_left + n_right; i++) {
|
|
if (diff[i] != vweight[i] - resid[i]) {
|
|
printf("ERROR: diff[%d] = %d, but vweight = %d and resid = %d\n", i, diff[i], vweight[i],
|
|
resid[i]);
|
|
}
|
|
}
|
|
|
|
sfree(diff);
|
|
}
|
|
|