mirror of https://github.com/YosysHQ/abc.git
1325 lines
33 KiB
C
1325 lines
33 KiB
C
/**CFile****************************************************************
|
|
|
|
FileName [utilBipart.c]
|
|
|
|
SystemName [ABC: Logic synthesis and verification system.]
|
|
|
|
PackageName [Handling counter-examples.]
|
|
|
|
Synopsis [Handling counter-examples.]
|
|
|
|
Author [Alan Mishchenko]
|
|
|
|
Affiliation [UC Berkeley]
|
|
|
|
Date [Ver. 1.0. Started - June 20, 2005.]
|
|
|
|
Revision [$Id: utilBipart.c,v 1.00 2005/06/20 00:00:00 alanmi Exp $]
|
|
|
|
***********************************************************************/
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <stdint.h>
|
|
#include <assert.h>
|
|
#include <time.h>
|
|
|
|
#include "misc/util/abc_global.h"
|
|
|
|
ABC_NAMESPACE_IMPL_START
|
|
|
|
////////////////////////////////////////////////////////////////////////
|
|
/// DECLARATIONS ///
|
|
////////////////////////////////////////////////////////////////////////
|
|
|
|
#define List_ForEachSet(pList, pSet, s) for ((s) = 0, (pSet) = (pList) + 1; (s) < (pList)[0]; ++(s), (pSet) += (pSet)[0] + 1)
|
|
#define Set_ForEachNode(pSet, node, n) for ((n) = 1; (n) <= (pSet)[0] && (((node) = (pSet)[n]), 1); ++(n))
|
|
|
|
////////////////////////////////////////////////////////////////////////
|
|
/// FUNCTION DEFINITIONS ///
|
|
////////////////////////////////////////////////////////////////////////
|
|
|
|
typedef struct {
|
|
int *data;
|
|
int size;
|
|
int cap;
|
|
} IntVector;
|
|
|
|
typedef struct {
|
|
int node_count;
|
|
int edge_count;
|
|
int *offset;
|
|
int *neighbors;
|
|
int *orig_ids;
|
|
} Graph;
|
|
|
|
typedef struct {
|
|
int sizeA;
|
|
int sizeB;
|
|
int *nodesA;
|
|
int *nodesB;
|
|
uint64_t hashA;
|
|
uint64_t hashB;
|
|
} ComponentRecord;
|
|
|
|
typedef struct {
|
|
ComponentRecord *data;
|
|
int size;
|
|
int cap;
|
|
} ComponentList;
|
|
|
|
typedef struct {
|
|
int u;
|
|
int v;
|
|
int assigned;
|
|
} EdgeInfo;
|
|
|
|
typedef struct {
|
|
uint64_t key;
|
|
int index;
|
|
} EdgeMapEntry;
|
|
|
|
typedef struct {
|
|
IntVector nodesA;
|
|
IntVector nodesB;
|
|
IntVector edges;
|
|
int edge_count;
|
|
} WorkComponent;
|
|
|
|
typedef struct {
|
|
Graph graph;
|
|
EdgeInfo *edges;
|
|
EdgeMapEntry *edge_map;
|
|
int edge_count;
|
|
WorkComponent *components;
|
|
int component_count;
|
|
} SolverContext;
|
|
|
|
static void validate_edge_ownership(const SolverContext *ctx) {
|
|
int *owner;
|
|
int i;
|
|
if (ctx->edge_count == 0) {
|
|
return;
|
|
}
|
|
owner = (int *)malloc(ctx->edge_count * sizeof(int));
|
|
assert(owner);
|
|
for (i = 0; i < ctx->edge_count; ++i) {
|
|
owner[i] = -1;
|
|
}
|
|
for (i = 0; i < ctx->component_count; ++i) {
|
|
const WorkComponent *comp = &ctx->components[i];
|
|
int j;
|
|
for (j = 0; j < comp->edges.size; ++j) {
|
|
int idx = comp->edges.data[j];
|
|
assert(idx >= 0 && idx < ctx->edge_count);
|
|
assert(owner[idx] == -1);
|
|
owner[idx] = i;
|
|
assert(ctx->edges[idx].assigned == i);
|
|
}
|
|
for (j = 1; j < comp->nodesA.size; ++j) {
|
|
assert(comp->nodesA.data[j - 1] < comp->nodesA.data[j]);
|
|
}
|
|
for (j = 1; j < comp->nodesB.size; ++j) {
|
|
assert(comp->nodesB.data[j - 1] < comp->nodesB.data[j]);
|
|
}
|
|
}
|
|
for (i = 0; i < ctx->edge_count; ++i) {
|
|
int assigned = ctx->edges[i].assigned;
|
|
if (assigned >= 0) {
|
|
assert(owner[i] == assigned);
|
|
} else {
|
|
assert(owner[i] == -1);
|
|
}
|
|
}
|
|
free(owner);
|
|
}
|
|
|
|
static void vec_init(IntVector *v) {
|
|
v->data = NULL;
|
|
v->size = 0;
|
|
v->cap = 0;
|
|
}
|
|
|
|
static void vec_reserve(IntVector *v, int need) {
|
|
if (need > v->cap) {
|
|
int cap = v->cap ? v->cap : 8;
|
|
while (cap < need) {
|
|
cap *= 2;
|
|
}
|
|
v->data = (int *)realloc(v->data, cap * sizeof(int));
|
|
assert(v->data);
|
|
v->cap = cap;
|
|
}
|
|
}
|
|
|
|
static void vec_push(IntVector *v, int value) {
|
|
vec_reserve(v, v->size + 1);
|
|
v->data[v->size++] = value;
|
|
}
|
|
|
|
static void vec_clear(IntVector *v) {
|
|
v->size = 0;
|
|
}
|
|
|
|
static void vec_free(IntVector *v) {
|
|
free(v->data);
|
|
v->data = NULL;
|
|
v->size = 0;
|
|
v->cap = 0;
|
|
}
|
|
|
|
static void component_list_init(ComponentList *list) {
|
|
list->data = NULL;
|
|
list->size = 0;
|
|
list->cap = 0;
|
|
}
|
|
|
|
static void component_list_push(ComponentList *list, ComponentRecord rec) {
|
|
if (list->size == list->cap) {
|
|
int cap = list->cap ? list->cap * 2 : 4;
|
|
list->data = (ComponentRecord *)realloc(list->data, cap * sizeof(ComponentRecord));
|
|
assert(list->data);
|
|
list->cap = cap;
|
|
}
|
|
list->data[list->size++] = rec;
|
|
}
|
|
|
|
static void component_list_free(ComponentList *list) {
|
|
int i;
|
|
for (i = 0; i < list->size; ++i) {
|
|
free(list->data[i].nodesA);
|
|
free(list->data[i].nodesB);
|
|
}
|
|
free(list->data);
|
|
list->data = NULL;
|
|
list->size = 0;
|
|
list->cap = 0;
|
|
}
|
|
|
|
static int cmp_int(const void *a, const void *b) {
|
|
const int lhs = *(const int *)a;
|
|
const int rhs = *(const int *)b;
|
|
return (lhs > rhs) - (lhs < rhs);
|
|
}
|
|
|
|
static int compare_sets(const int *a, int sizeA, const int *b, int sizeB) {
|
|
int i;
|
|
if (sizeA < sizeB) {
|
|
return -1;
|
|
}
|
|
if (sizeA > sizeB) {
|
|
return 1;
|
|
}
|
|
for (i = 0; i < sizeA; ++i) {
|
|
if (a[i] < b[i]) {
|
|
return -1;
|
|
}
|
|
if (a[i] > b[i]) {
|
|
return 1;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int threshold_value(int percent, int count) {
|
|
return (percent * count + 99) / 100;
|
|
}
|
|
|
|
static uint64_t hash_ids(const int *ids, int count) {
|
|
uint64_t h = 1469598103934665603ULL;
|
|
uint64_t prime = 1099511628211ULL;
|
|
int i;
|
|
for (i = 0; i < count; ++i) {
|
|
h ^= (uint64_t)(uint32_t)ids[i] + 0x9e3779b97f4a7c15ULL;
|
|
h *= prime;
|
|
}
|
|
return h;
|
|
}
|
|
|
|
static int binary_search_id(const int *ids, int count, int value) {
|
|
int lo = 0;
|
|
int hi = count - 1;
|
|
while (lo <= hi) {
|
|
int mid = lo + (hi - lo) / 2;
|
|
if (ids[mid] == value) {
|
|
return mid;
|
|
}
|
|
if (ids[mid] < value) {
|
|
lo = mid + 1;
|
|
} else {
|
|
hi = mid - 1;
|
|
}
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
static void graph_free(Graph *g) {
|
|
free(g->offset);
|
|
free(g->neighbors);
|
|
free(g->orig_ids);
|
|
g->offset = NULL;
|
|
g->neighbors = NULL;
|
|
g->orig_ids = NULL;
|
|
g->node_count = 0;
|
|
g->edge_count = 0;
|
|
}
|
|
|
|
static void graph_build(Graph *g, int *pairs, int pair_count) {
|
|
int *ids;
|
|
int id_count = 0;
|
|
int unique_ids = 0;
|
|
int i;
|
|
int edge_capacity;
|
|
int *edges_u;
|
|
int *edges_v;
|
|
int mapped_edges = 0;
|
|
int *degree;
|
|
int *offset;
|
|
int *neighbors;
|
|
int total_adj;
|
|
int *cursor;
|
|
int *new_offsets;
|
|
int write_pos;
|
|
|
|
g->node_count = 0;
|
|
g->edge_count = 0;
|
|
g->offset = NULL;
|
|
g->neighbors = NULL;
|
|
g->orig_ids = NULL;
|
|
|
|
if (pair_count == 0) {
|
|
g->offset = (int *)calloc(1, sizeof(int));
|
|
assert(g->offset);
|
|
return;
|
|
}
|
|
|
|
assert((pair_count & 1) == 0);
|
|
edge_capacity = pair_count / 2;
|
|
|
|
ids = (int *)malloc(pair_count * sizeof(int));
|
|
assert(ids);
|
|
edges_u = (int *)malloc(edge_capacity * sizeof(int));
|
|
edges_v = (int *)malloc(edge_capacity * sizeof(int));
|
|
assert(edges_u && edges_v);
|
|
|
|
for (i = 0; i < pair_count; i += 2) {
|
|
ids[id_count++] = pairs[i];
|
|
ids[id_count++] = pairs[i + 1];
|
|
}
|
|
|
|
qsort(ids, id_count, sizeof(int), cmp_int);
|
|
for (i = 0; i < id_count; ++i) {
|
|
if (i == 0 || ids[i] != ids[i - 1]) {
|
|
ids[unique_ids++] = ids[i];
|
|
}
|
|
}
|
|
|
|
g->node_count = unique_ids;
|
|
g->orig_ids = (int *)malloc(unique_ids * sizeof(int));
|
|
assert(g->orig_ids);
|
|
for (i = 0; i < unique_ids; ++i) {
|
|
g->orig_ids[i] = ids[i];
|
|
}
|
|
|
|
for (i = 0; i < pair_count; i += 2) {
|
|
int a = pairs[i];
|
|
int b = pairs[i + 1];
|
|
int u = binary_search_id(g->orig_ids, unique_ids, a);
|
|
int v = binary_search_id(g->orig_ids, unique_ids, b);
|
|
if (u == v) {
|
|
continue;
|
|
}
|
|
edges_u[mapped_edges] = u;
|
|
edges_v[mapped_edges] = v;
|
|
mapped_edges++;
|
|
}
|
|
|
|
degree = (int *)calloc(unique_ids, sizeof(int));
|
|
assert(degree);
|
|
for (i = 0; i < mapped_edges; ++i) {
|
|
int u = edges_u[i];
|
|
int v = edges_v[i];
|
|
degree[u]++;
|
|
degree[v]++;
|
|
}
|
|
|
|
offset = (int *)malloc((unique_ids + 1) * sizeof(int));
|
|
assert(offset);
|
|
offset[0] = 0;
|
|
for (i = 0; i < unique_ids; ++i) {
|
|
offset[i + 1] = offset[i] + degree[i];
|
|
}
|
|
total_adj = offset[unique_ids];
|
|
neighbors = (int *)malloc(total_adj * sizeof(int));
|
|
assert(neighbors);
|
|
|
|
cursor = (int *)malloc(unique_ids * sizeof(int));
|
|
assert(cursor);
|
|
memcpy(cursor, offset, unique_ids * sizeof(int));
|
|
for (i = 0; i < mapped_edges; ++i) {
|
|
int u = edges_u[i];
|
|
int v = edges_v[i];
|
|
neighbors[cursor[u]++] = v;
|
|
neighbors[cursor[v]++] = u;
|
|
}
|
|
free(cursor);
|
|
free(edges_u);
|
|
free(edges_v);
|
|
free(degree);
|
|
|
|
new_offsets = (int *)malloc((unique_ids + 1) * sizeof(int));
|
|
assert(new_offsets);
|
|
new_offsets[0] = 0;
|
|
write_pos = 0;
|
|
for (i = 0; i < unique_ids; ++i) {
|
|
int start = offset[i];
|
|
int end = offset[i + 1];
|
|
int len = end - start;
|
|
int j;
|
|
if (len > 1) {
|
|
qsort(neighbors + start, len, sizeof(int), cmp_int);
|
|
}
|
|
{
|
|
int last = -1;
|
|
for (j = start; j < end; ++j) {
|
|
int v = neighbors[j];
|
|
if (v == i) {
|
|
continue;
|
|
}
|
|
if (last != -1 && v == last) {
|
|
continue;
|
|
}
|
|
neighbors[write_pos++] = v;
|
|
last = v;
|
|
}
|
|
}
|
|
new_offsets[i + 1] = write_pos;
|
|
}
|
|
|
|
free(offset);
|
|
free(ids);
|
|
|
|
g->offset = new_offsets;
|
|
g->neighbors = neighbors;
|
|
g->edge_count = write_pos / 2;
|
|
}
|
|
|
|
static int count_edges_to_mark(const Graph *g, int node, const unsigned char *mark) {
|
|
int start = g->offset[node];
|
|
int end = g->offset[node + 1];
|
|
int count = 0;
|
|
int i;
|
|
for (i = start; i < end; ++i) {
|
|
int v = g->neighbors[i];
|
|
if (mark[v]) {
|
|
count++;
|
|
}
|
|
}
|
|
return count;
|
|
}
|
|
|
|
static int component_exists(const ComponentList *list, uint64_t hashA, uint64_t hashB, const int *nodesA, int sizeA, const int *nodesB, int sizeB) {
|
|
int i;
|
|
for (i = 0; i < list->size; ++i) {
|
|
const ComponentRecord *rec = &list->data[i];
|
|
if (rec->sizeA != sizeA || rec->sizeB != sizeB) {
|
|
continue;
|
|
}
|
|
if (rec->hashA != hashA || rec->hashB != hashB) {
|
|
continue;
|
|
}
|
|
if (memcmp(rec->nodesA, nodesA, sizeA * sizeof(int)) != 0) {
|
|
continue;
|
|
}
|
|
if (memcmp(rec->nodesB, nodesB, sizeB * sizeof(int)) != 0) {
|
|
continue;
|
|
}
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static void detect_components(const Graph *g, int min_con, int min_part, ComponentList *out) {
|
|
unsigned char *markA;
|
|
unsigned char *markB;
|
|
int *counts;
|
|
IntVector setA;
|
|
IntVector setB;
|
|
IntVector touchedA;
|
|
IntVector touchedB;
|
|
IntVector touchedCounts;
|
|
int seed;
|
|
|
|
if (g->node_count == 0) {
|
|
return;
|
|
}
|
|
|
|
markA = (unsigned char *)calloc(g->node_count, sizeof(unsigned char));
|
|
markB = (unsigned char *)calloc(g->node_count, sizeof(unsigned char));
|
|
counts = (int *)calloc(g->node_count, sizeof(int));
|
|
assert(markA && markB && counts);
|
|
|
|
vec_init(&setA);
|
|
vec_init(&setB);
|
|
vec_init(&touchedA);
|
|
vec_init(&touchedB);
|
|
vec_init(&touchedCounts);
|
|
|
|
for (seed = 0; seed < g->node_count; ++seed) {
|
|
int start = g->offset[seed];
|
|
int end = g->offset[seed + 1];
|
|
int iter = 0;
|
|
int valid = 1;
|
|
int changed;
|
|
int thresholdA;
|
|
int thresholdB;
|
|
int i;
|
|
|
|
if (end - start == 0) {
|
|
continue;
|
|
}
|
|
|
|
vec_clear(&setA);
|
|
vec_clear(&setB);
|
|
vec_clear(&touchedA);
|
|
vec_clear(&touchedB);
|
|
vec_clear(&touchedCounts);
|
|
|
|
markA[seed] = 1;
|
|
vec_push(&touchedA, seed);
|
|
vec_push(&setA, seed);
|
|
|
|
for (i = start; i < end; ++i) {
|
|
int v = g->neighbors[i];
|
|
if (!markB[v]) {
|
|
markB[v] = 1;
|
|
vec_push(&touchedB, v);
|
|
vec_push(&setB, v);
|
|
}
|
|
}
|
|
|
|
if (setB.size == 0) {
|
|
goto cleanup_seed;
|
|
}
|
|
|
|
while (valid) {
|
|
changed = 0;
|
|
thresholdA = threshold_value(min_con, setB.size);
|
|
if (thresholdA == 0 && setB.size > 0) {
|
|
thresholdA = 1;
|
|
}
|
|
|
|
for (i = 0; i < setA.size;) {
|
|
int node = setA.data[i];
|
|
int cnt = count_edges_to_mark(g, node, markB);
|
|
if (cnt < thresholdA) {
|
|
markA[node] = 0;
|
|
setA.data[i] = setA.data[setA.size - 1];
|
|
setA.size--;
|
|
changed = 1;
|
|
continue;
|
|
}
|
|
++i;
|
|
}
|
|
|
|
if (setA.size == 0) {
|
|
valid = 0;
|
|
break;
|
|
}
|
|
|
|
thresholdB = threshold_value(min_con, setA.size);
|
|
if (thresholdB == 0 && setA.size > 0) {
|
|
thresholdB = 1;
|
|
}
|
|
|
|
for (i = 0; i < setB.size;) {
|
|
int node = setB.data[i];
|
|
int cnt = count_edges_to_mark(g, node, markA);
|
|
if (cnt < thresholdB) {
|
|
markB[node] = 0;
|
|
setB.data[i] = setB.data[setB.size - 1];
|
|
setB.size--;
|
|
changed = 1;
|
|
continue;
|
|
}
|
|
++i;
|
|
}
|
|
|
|
if (setB.size == 0) {
|
|
valid = 0;
|
|
break;
|
|
}
|
|
|
|
if (setB.size > 0 && thresholdA > 0) {
|
|
for (i = 0; i < setB.size; ++i) {
|
|
int node = setB.data[i];
|
|
int n;
|
|
for (n = g->offset[node]; n < g->offset[node + 1]; ++n) {
|
|
int v = g->neighbors[n];
|
|
if (markA[v] || markB[v]) {
|
|
continue;
|
|
}
|
|
if (counts[v] == 0) {
|
|
vec_push(&touchedCounts, v);
|
|
}
|
|
counts[v]++;
|
|
}
|
|
}
|
|
|
|
for (i = 0; i < touchedCounts.size; ++i) {
|
|
int node = touchedCounts.data[i];
|
|
if (counts[node] >= thresholdA) {
|
|
markA[node] = 1;
|
|
vec_push(&touchedA, node);
|
|
vec_push(&setA, node);
|
|
changed = 1;
|
|
}
|
|
counts[node] = 0;
|
|
}
|
|
vec_clear(&touchedCounts);
|
|
}
|
|
|
|
thresholdB = threshold_value(min_con, setA.size);
|
|
if (thresholdB == 0 && setA.size > 0) {
|
|
thresholdB = 1;
|
|
}
|
|
|
|
if (setA.size > 0 && thresholdB > 0) {
|
|
for (i = 0; i < setA.size; ++i) {
|
|
int node = setA.data[i];
|
|
int n;
|
|
for (n = g->offset[node]; n < g->offset[node + 1]; ++n) {
|
|
int v = g->neighbors[n];
|
|
if (markA[v] || markB[v]) {
|
|
continue;
|
|
}
|
|
if (counts[v] == 0) {
|
|
vec_push(&touchedCounts, v);
|
|
}
|
|
counts[v]++;
|
|
}
|
|
}
|
|
|
|
for (i = 0; i < touchedCounts.size; ++i) {
|
|
int node = touchedCounts.data[i];
|
|
if (counts[node] >= thresholdB) {
|
|
markB[node] = 1;
|
|
vec_push(&touchedB, node);
|
|
vec_push(&setB, node);
|
|
changed = 1;
|
|
}
|
|
counts[node] = 0;
|
|
}
|
|
vec_clear(&touchedCounts);
|
|
}
|
|
|
|
if (!changed) {
|
|
break;
|
|
}
|
|
|
|
++iter;
|
|
if (iter > 64) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (valid && setA.size >= min_part && setB.size >= min_part) {
|
|
int sizeA = setA.size;
|
|
int sizeB = setB.size;
|
|
int64_t edges = 0;
|
|
int *idsA = (int *)malloc(sizeA * sizeof(int));
|
|
int *idsB = (int *)malloc(sizeB * sizeof(int));
|
|
uint64_t hashA;
|
|
uint64_t hashB;
|
|
|
|
assert(idsA && idsB);
|
|
|
|
for (i = 0; i < sizeA; ++i) {
|
|
int node = setA.data[i];
|
|
edges += count_edges_to_mark(g, node, markB);
|
|
idsA[i] = node;
|
|
}
|
|
|
|
for (i = 0; i < sizeB; ++i) {
|
|
idsB[i] = setB.data[i];
|
|
}
|
|
|
|
if (edges * 100 >= (int64_t)min_con * sizeA * sizeB) {
|
|
qsort(idsA, sizeA, sizeof(int), cmp_int);
|
|
qsort(idsB, sizeB, sizeof(int), cmp_int);
|
|
if (compare_sets(idsA, sizeA, idsB, sizeB) > 0) {
|
|
int *tmp = idsA;
|
|
int tmp_size = sizeA;
|
|
idsA = idsB;
|
|
idsB = tmp;
|
|
sizeA = sizeB;
|
|
sizeB = tmp_size;
|
|
}
|
|
hashA = hash_ids(idsA, sizeA);
|
|
hashB = hash_ids(idsB, sizeB);
|
|
if (!component_exists(out, hashA, hashB, idsA, sizeA, idsB, sizeB)) {
|
|
ComponentRecord rec;
|
|
rec.sizeA = sizeA;
|
|
rec.sizeB = sizeB;
|
|
rec.nodesA = idsA;
|
|
rec.nodesB = idsB;
|
|
rec.hashA = hashA;
|
|
rec.hashB = hashB;
|
|
component_list_push(out, rec);
|
|
} else {
|
|
free(idsA);
|
|
free(idsB);
|
|
}
|
|
} else {
|
|
free(idsA);
|
|
free(idsB);
|
|
}
|
|
}
|
|
|
|
cleanup_seed:
|
|
for (i = 0; i < touchedA.size; ++i) {
|
|
markA[touchedA.data[i]] = 0;
|
|
}
|
|
for (i = 0; i < touchedB.size; ++i) {
|
|
markB[touchedB.data[i]] = 0;
|
|
}
|
|
for (i = 0; i < touchedCounts.size; ++i) {
|
|
counts[touchedCounts.data[i]] = 0;
|
|
}
|
|
}
|
|
|
|
vec_free(&setA);
|
|
vec_free(&setB);
|
|
vec_free(&touchedA);
|
|
vec_free(&touchedB);
|
|
vec_free(&touchedCounts);
|
|
free(markA);
|
|
free(markB);
|
|
free(counts);
|
|
}
|
|
|
|
static uint64_t make_edge_key(int a, int b) {
|
|
uint32_t ua = (uint32_t)a;
|
|
uint32_t ub = (uint32_t)b;
|
|
if (ua < ub) {
|
|
return ((uint64_t)ua << 32) | (uint64_t)ub;
|
|
}
|
|
return ((uint64_t)ub << 32) | (uint64_t)ua;
|
|
}
|
|
|
|
static int cmp_edge_map(const void *a, const void *b) {
|
|
const EdgeMapEntry *ea = (const EdgeMapEntry *)a;
|
|
const EdgeMapEntry *eb = (const EdgeMapEntry *)b;
|
|
if (ea->key < eb->key) {
|
|
return -1;
|
|
}
|
|
if (ea->key > eb->key) {
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int edge_map_find(const EdgeMapEntry *map, int size, int u, int v) {
|
|
uint64_t key = make_edge_key(u, v);
|
|
int lo = 0;
|
|
int hi = size - 1;
|
|
while (lo <= hi) {
|
|
int mid = lo + (hi - lo) / 2;
|
|
if (map[mid].key == key) {
|
|
return map[mid].index;
|
|
}
|
|
if (map[mid].key < key) {
|
|
lo = mid + 1;
|
|
} else {
|
|
hi = mid - 1;
|
|
}
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
static void work_component_init(WorkComponent *comp) {
|
|
vec_init(&comp->nodesA);
|
|
vec_init(&comp->nodesB);
|
|
vec_init(&comp->edges);
|
|
comp->edge_count = 0;
|
|
}
|
|
|
|
static void work_component_free(WorkComponent *comp) {
|
|
vec_free(&comp->nodesA);
|
|
vec_free(&comp->nodesB);
|
|
vec_free(&comp->edges);
|
|
comp->edge_count = 0;
|
|
}
|
|
|
|
static int vector_contains(const IntVector *vec, int value) {
|
|
int lo = 0;
|
|
int hi = vec->size - 1;
|
|
while (lo <= hi) {
|
|
int mid = lo + (hi - lo) / 2;
|
|
int mid_val = vec->data[mid];
|
|
if (mid_val == value) {
|
|
return 1;
|
|
}
|
|
if (mid_val < value) {
|
|
lo = mid + 1;
|
|
} else {
|
|
hi = mid - 1;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static void vector_insert_sorted(IntVector *vec, int value) {
|
|
int lo = 0;
|
|
int hi = vec->size;
|
|
while (lo < hi) {
|
|
int mid = lo + (hi - lo) / 2;
|
|
if (vec->data[mid] < value) {
|
|
lo = mid + 1;
|
|
} else {
|
|
hi = mid;
|
|
}
|
|
}
|
|
vec_reserve(vec, vec->size + 1);
|
|
if (lo < vec->size) {
|
|
memmove(&vec->data[lo + 1], &vec->data[lo], (vec->size - lo) * sizeof(int));
|
|
}
|
|
vec->data[lo] = value;
|
|
vec->size++;
|
|
}
|
|
|
|
static EdgeInfo *build_edge_list(const Graph *g, int *out_count) {
|
|
int total = g->edge_count;
|
|
EdgeInfo *edges;
|
|
int idx = 0;
|
|
int u;
|
|
|
|
if (total == 0) {
|
|
*out_count = 0;
|
|
return NULL;
|
|
}
|
|
|
|
edges = (EdgeInfo *)malloc(total * sizeof(EdgeInfo));
|
|
assert(edges);
|
|
|
|
for (u = 0; u < g->node_count; ++u) {
|
|
int start = g->offset[u];
|
|
int end = g->offset[u + 1];
|
|
int i;
|
|
for (i = start; i < end; ++i) {
|
|
int v = g->neighbors[i];
|
|
if (u < v) {
|
|
edges[idx].u = u;
|
|
edges[idx].v = v;
|
|
edges[idx].assigned = -1;
|
|
idx++;
|
|
}
|
|
}
|
|
}
|
|
|
|
assert(idx == total);
|
|
*out_count = total;
|
|
return edges;
|
|
}
|
|
|
|
static EdgeMapEntry *build_edge_map(const EdgeInfo *edges, int edge_count) {
|
|
EdgeMapEntry *map;
|
|
int i;
|
|
if (edge_count == 0) {
|
|
return NULL;
|
|
}
|
|
map = (EdgeMapEntry *)malloc(edge_count * sizeof(EdgeMapEntry));
|
|
assert(map);
|
|
for (i = 0; i < edge_count; ++i) {
|
|
map[i].key = make_edge_key(edges[i].u, edges[i].v);
|
|
map[i].index = i;
|
|
}
|
|
qsort(map, edge_count, sizeof(EdgeMapEntry), cmp_edge_map);
|
|
return map;
|
|
}
|
|
|
|
static int collect_edge_candidate(const EdgeInfo *edges, EdgeMapEntry *map, int map_size, int u, int v, int comp_idx, IntVector *list, int *assigned) {
|
|
int idx = edge_map_find(map, map_size, u, v);
|
|
if (idx < 0) {
|
|
return 0;
|
|
}
|
|
if (edges[idx].assigned == comp_idx) {
|
|
return 0;
|
|
}
|
|
if (edges[idx].assigned != -1) {
|
|
return 0;
|
|
}
|
|
vec_push(list, idx);
|
|
(*assigned)++;
|
|
return 1;
|
|
}
|
|
|
|
static int evaluate_orientation(const EdgeInfo *edges, EdgeMapEntry *map, int map_size, WorkComponent *comp, int comp_idx, int nodeA, int nodeB, int min_con, IntVector *tmp_edges, int *addA, int *addB, int *num, int *den) {
|
|
int inA = vector_contains(&comp->nodesA, nodeA);
|
|
int inB = vector_contains(&comp->nodesB, nodeB);
|
|
int add_u = inA ? 0 : 1;
|
|
int add_v = inB ? 0 : 1;
|
|
int new_sizeA;
|
|
int new_sizeB;
|
|
int new_edges = 0;
|
|
int numerator;
|
|
|
|
if (!inA && vector_contains(&comp->nodesB, nodeA)) {
|
|
return 0;
|
|
}
|
|
if (!inB && vector_contains(&comp->nodesA, nodeB)) {
|
|
return 0;
|
|
}
|
|
|
|
new_sizeA = comp->nodesA.size + add_u;
|
|
new_sizeB = comp->nodesB.size + add_v;
|
|
if (new_sizeA == 0 || new_sizeB == 0) {
|
|
return 0;
|
|
}
|
|
|
|
vec_clear(tmp_edges);
|
|
|
|
if (add_u) {
|
|
int i;
|
|
for (i = 0; i < comp->nodesB.size; ++i) {
|
|
collect_edge_candidate(edges, map, map_size, nodeA, comp->nodesB.data[i], comp_idx, tmp_edges, &new_edges);
|
|
}
|
|
}
|
|
|
|
if (add_v) {
|
|
int i;
|
|
for (i = 0; i < comp->nodesA.size; ++i) {
|
|
collect_edge_candidate(edges, map, map_size, comp->nodesA.data[i], nodeB, comp_idx, tmp_edges, &new_edges);
|
|
}
|
|
}
|
|
|
|
if (add_u && add_v) {
|
|
collect_edge_candidate(edges, map, map_size, nodeA, nodeB, comp_idx, tmp_edges, &new_edges);
|
|
}
|
|
|
|
if (!add_u && !add_v) {
|
|
collect_edge_candidate(edges, map, map_size, nodeA, nodeB, comp_idx, tmp_edges, &new_edges);
|
|
}
|
|
|
|
numerator = comp->edge_count + new_edges;
|
|
*den = new_sizeA * new_sizeB;
|
|
*num = numerator;
|
|
|
|
if ((int64_t)numerator * 100 < (int64_t)min_con * (*den)) {
|
|
return 0;
|
|
}
|
|
|
|
*addA = add_u;
|
|
*addB = add_v;
|
|
return 1;
|
|
}
|
|
|
|
static void assign_edges_to_component(EdgeInfo *edges, WorkComponent *comp, IntVector *edge_indices, int comp_idx) {
|
|
int i;
|
|
for (i = 0; i < edge_indices->size; ++i) {
|
|
int idx = edge_indices->data[i];
|
|
edges[idx].assigned = comp_idx;
|
|
vec_push(&comp->edges, idx);
|
|
comp->edge_count++;
|
|
}
|
|
}
|
|
|
|
static int compare_core_desc(const void *a, const void *b) {
|
|
const ComponentRecord *ra = (const ComponentRecord *)a;
|
|
const ComponentRecord *rb = (const ComponentRecord *)b;
|
|
int64_t ea = (int64_t)ra->sizeA * (int64_t)ra->sizeB;
|
|
int64_t eb = (int64_t)rb->sizeA * (int64_t)rb->sizeB;
|
|
if (ea > eb) {
|
|
return -1;
|
|
}
|
|
if (ea < eb) {
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static void build_core_components(EdgeInfo *edges, EdgeMapEntry *map, int map_size, int min_part_size, ComponentList *cores, WorkComponent **out_components, int *out_count) {
|
|
WorkComponent *components;
|
|
int comp_count = 0;
|
|
int capacity = 4;
|
|
int i;
|
|
|
|
components = (WorkComponent *)malloc(capacity * sizeof(WorkComponent));
|
|
assert(components);
|
|
|
|
for (i = 0; i < cores->size; ++i) {
|
|
ComponentRecord *rec = &cores->data[i];
|
|
int ok = 1;
|
|
int a;
|
|
if (rec->sizeA < min_part_size || rec->sizeB < min_part_size) {
|
|
continue;
|
|
}
|
|
for (a = 0; a < rec->sizeA && ok; ++a) {
|
|
int b;
|
|
for (b = 0; b < rec->sizeB; ++b) {
|
|
int idx = edge_map_find(map, map_size, rec->nodesA[a], rec->nodesB[b]);
|
|
if (idx < 0 || edges[idx].assigned != -1) {
|
|
ok = 0;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
if (!ok) {
|
|
continue;
|
|
}
|
|
if (comp_count == capacity) {
|
|
capacity *= 2;
|
|
components = (WorkComponent *)realloc(components, capacity * sizeof(WorkComponent));
|
|
assert(components);
|
|
}
|
|
work_component_init(&components[comp_count]);
|
|
for (a = 0; a < rec->sizeA; ++a) {
|
|
vector_insert_sorted(&components[comp_count].nodesA, rec->nodesA[a]);
|
|
}
|
|
for (a = 0; a < rec->sizeB; ++a) {
|
|
vector_insert_sorted(&components[comp_count].nodesB, rec->nodesB[a]);
|
|
}
|
|
{
|
|
int ai;
|
|
for (ai = 0; ai < rec->sizeA; ++ai) {
|
|
int bi;
|
|
for (bi = 0; bi < rec->sizeB; ++bi) {
|
|
int idx = edge_map_find(map, map_size, rec->nodesA[ai], rec->nodesB[bi]);
|
|
assert(idx >= 0 && edges[idx].assigned == -1);
|
|
edges[idx].assigned = comp_count;
|
|
vec_push(&components[comp_count].edges, idx);
|
|
components[comp_count].edge_count++;
|
|
}
|
|
}
|
|
}
|
|
comp_count++;
|
|
}
|
|
|
|
*out_components = components;
|
|
*out_count = comp_count;
|
|
}
|
|
|
|
static void expand_components(EdgeInfo *edges, EdgeMapEntry *map, int map_size, WorkComponent *components, int comp_count, int edge_count, int min_con_value) {
|
|
IntVector tmp_edges;
|
|
IntVector best_edges;
|
|
int progress = 1;
|
|
|
|
vec_init(&tmp_edges);
|
|
vec_init(&best_edges);
|
|
|
|
while (progress) {
|
|
int e;
|
|
progress = 0;
|
|
for (e = 0; e < edge_count; ++e) {
|
|
int u;
|
|
int v;
|
|
int best_comp = -1;
|
|
int best_addA = 0;
|
|
int best_addB = 0;
|
|
int best_num = 0;
|
|
int best_den = 1;
|
|
int best_nodeA = -1;
|
|
int best_nodeB = -1;
|
|
int comp_idx;
|
|
|
|
if (edges[e].assigned != -1) {
|
|
continue;
|
|
}
|
|
u = edges[e].u;
|
|
v = edges[e].v;
|
|
vec_clear(&best_edges);
|
|
|
|
for (comp_idx = 0; comp_idx < comp_count; ++comp_idx) {
|
|
WorkComponent *comp = &components[comp_idx];
|
|
int addA;
|
|
int addB;
|
|
int num;
|
|
int den;
|
|
if (evaluate_orientation(edges, map, map_size, comp, comp_idx, u, v, min_con_value, &tmp_edges, &addA, &addB, &num, &den)) {
|
|
int better = 0;
|
|
if ((int64_t)num * best_den > (int64_t)best_num * den) {
|
|
better = 1;
|
|
} else if ((int64_t)num * best_den == (int64_t)best_num * den && num > best_num) {
|
|
better = 1;
|
|
}
|
|
if (better) {
|
|
best_comp = comp_idx;
|
|
best_addA = addA;
|
|
best_addB = addB;
|
|
best_num = num;
|
|
best_den = den;
|
|
best_nodeA = u;
|
|
best_nodeB = v;
|
|
vec_clear(&best_edges);
|
|
vec_reserve(&best_edges, tmp_edges.size);
|
|
memcpy(best_edges.data, tmp_edges.data, tmp_edges.size * sizeof(int));
|
|
best_edges.size = tmp_edges.size;
|
|
}
|
|
}
|
|
if (evaluate_orientation(edges, map, map_size, comp, comp_idx, v, u, min_con_value, &tmp_edges, &addA, &addB, &num, &den)) {
|
|
int better = 0;
|
|
if ((int64_t)num * best_den > (int64_t)best_num * den) {
|
|
better = 1;
|
|
} else if ((int64_t)num * best_den == (int64_t)best_num * den && num > best_num) {
|
|
better = 1;
|
|
}
|
|
if (better) {
|
|
best_comp = comp_idx;
|
|
best_addA = addA;
|
|
best_addB = addB;
|
|
best_num = num;
|
|
best_den = den;
|
|
best_nodeA = v;
|
|
best_nodeB = u;
|
|
vec_clear(&best_edges);
|
|
vec_reserve(&best_edges, tmp_edges.size);
|
|
memcpy(best_edges.data, tmp_edges.data, tmp_edges.size * sizeof(int));
|
|
best_edges.size = tmp_edges.size;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (best_comp != -1) {
|
|
WorkComponent *comp = &components[best_comp];
|
|
if (best_addA && !vector_contains(&comp->nodesA, best_nodeA)) {
|
|
vector_insert_sorted(&comp->nodesA, best_nodeA);
|
|
}
|
|
if (best_addB && !vector_contains(&comp->nodesB, best_nodeB)) {
|
|
vector_insert_sorted(&comp->nodesB, best_nodeB);
|
|
}
|
|
assign_edges_to_component(edges, comp, &best_edges, best_comp);
|
|
progress = 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
vec_free(&tmp_edges);
|
|
vec_free(&best_edges);
|
|
}
|
|
|
|
static SolverContext * solver_run(int *pairs, int pair_count, int min_con_value, int min_part_size) {
|
|
SolverContext *ctx;
|
|
ComponentList cores;
|
|
ctx = (SolverContext *)calloc(1, sizeof(SolverContext));
|
|
assert(ctx);
|
|
|
|
graph_build(&ctx->graph, pairs, pair_count);
|
|
ctx->edges = build_edge_list(&ctx->graph, &ctx->edge_count);
|
|
ctx->edge_map = build_edge_map(ctx->edges, ctx->edge_count);
|
|
|
|
component_list_init(&cores);
|
|
detect_components(&ctx->graph, 100, min_part_size, &cores);
|
|
if (cores.size > 1) {
|
|
qsort(cores.data, cores.size, sizeof(ComponentRecord), compare_core_desc);
|
|
}
|
|
build_core_components(ctx->edges, ctx->edge_map, ctx->edge_count, min_part_size, &cores, &ctx->components, &ctx->component_count);
|
|
expand_components(ctx->edges, ctx->edge_map, ctx->edge_count, ctx->components, ctx->component_count, ctx->edge_count, min_con_value);
|
|
validate_edge_ownership(ctx);
|
|
|
|
component_list_free(&cores);
|
|
return ctx;
|
|
}
|
|
|
|
static int * solver_build_result_array(const SolverContext *ctx) {
|
|
int total_sets = ctx->component_count * 2;
|
|
int total_ints = 1;
|
|
int *result;
|
|
int offset = 1;
|
|
int c;
|
|
|
|
for (c = 0; c < ctx->component_count; ++c) {
|
|
total_ints += 1 + ctx->components[c].nodesA.size;
|
|
total_ints += 1 + ctx->components[c].nodesB.size;
|
|
}
|
|
|
|
result = (int *)malloc(total_ints * sizeof(int));
|
|
assert(result);
|
|
result[0] = total_sets;
|
|
|
|
for (c = 0; c < ctx->component_count; ++c) {
|
|
int i;
|
|
result[offset++] = ctx->components[c].nodesA.size;
|
|
for (i = 0; i < ctx->components[c].nodesA.size; ++i) {
|
|
int node = ctx->components[c].nodesA.data[i];
|
|
result[offset++] = ctx->graph.orig_ids[node];
|
|
}
|
|
result[offset++] = ctx->components[c].nodesB.size;
|
|
for (i = 0; i < ctx->components[c].nodesB.size; ++i) {
|
|
int node = ctx->components[c].nodesB.data[i];
|
|
result[offset++] = ctx->graph.orig_ids[node];
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
static void solver_free_context(SolverContext *ctx) {
|
|
int i;
|
|
if (!ctx) {
|
|
return;
|
|
}
|
|
for (i = 0; i < ctx->component_count; ++i) {
|
|
work_component_free(&ctx->components[i]);
|
|
}
|
|
free(ctx->components);
|
|
free(ctx->edges);
|
|
free(ctx->edge_map);
|
|
graph_free(&ctx->graph);
|
|
free(ctx);
|
|
}
|
|
|
|
static void print_components(const SolverContext *ctx) {
|
|
int c;
|
|
if (ctx->component_count == 0) {
|
|
printf("The number of components found is 0.\n");
|
|
return;
|
|
}
|
|
printf("The number of components found is %d:\n", ctx->component_count);
|
|
for (c = 0; c < ctx->component_count; ++c) {
|
|
const WorkComponent *comp = &ctx->components[c];
|
|
int i;
|
|
printf("Component %d:\n", c + 1);
|
|
printf(" { ");
|
|
for (i = 0; i < comp->nodesA.size; ++i) {
|
|
printf("%d ", ctx->graph.orig_ids[comp->nodesA.data[i]]);
|
|
}
|
|
printf("}\n");
|
|
printf(" { ");
|
|
for (i = 0; i < comp->nodesB.size; ++i) {
|
|
printf("%d ", ctx->graph.orig_ids[comp->nodesB.data[i]]);
|
|
}
|
|
printf("}\n");
|
|
|
|
printf(" ");
|
|
for (i = 0; i < comp->nodesA.size; ++i) {
|
|
printf("%6d", ctx->graph.orig_ids[comp->nodesA.data[i]]);
|
|
}
|
|
printf("\n");
|
|
{
|
|
int row;
|
|
for (row = 0; row < comp->nodesB.size; ++row) {
|
|
int nodeB = comp->nodesB.data[row];
|
|
int col;
|
|
printf("%6d", ctx->graph.orig_ids[nodeB]);
|
|
for (col = 0; col < comp->nodesA.size; ++col) {
|
|
int nodeA = comp->nodesA.data[col];
|
|
int idx = edge_map_find(ctx->edge_map, ctx->edge_count, nodeA, nodeB);
|
|
int mark = 0;
|
|
if (idx >= 0 && ctx->edges[idx].assigned == c) {
|
|
mark = 1;
|
|
}
|
|
printf("%6c", mark ? 'x' : ' ');
|
|
}
|
|
printf("\n");
|
|
}
|
|
}
|
|
printf("\n");
|
|
}
|
|
}
|
|
|
|
int * compute_bipartite_subgraphs(int *pArray, int nSize, int min_con_value, int min_part_size, int fVerbose) {
|
|
SolverContext *ctx = solver_run(pArray, nSize, min_con_value, min_part_size);
|
|
int *result = solver_build_result_array(ctx);
|
|
if ( fVerbose ) print_components(ctx);
|
|
solver_free_context(ctx);
|
|
return result;
|
|
}
|
|
|
|
/*
|
|
|
|
static int * read_edge_list(const char *filename, int *out_size) {
|
|
FILE *fp = fopen(filename, "r");
|
|
int capacity = 1024;
|
|
int count = 0;
|
|
int value;
|
|
int *data;
|
|
assert(fp);
|
|
|
|
data = (int *)malloc(capacity * sizeof(int));
|
|
assert(data);
|
|
while (fscanf(fp, "%d", &value) == 1) {
|
|
if (count == capacity) {
|
|
capacity *= 2;
|
|
data = (int *)realloc(data, capacity * sizeof(int));
|
|
assert(data);
|
|
}
|
|
data[count++] = value;
|
|
}
|
|
fclose(fp);
|
|
assert((count & 1) == 0);
|
|
*out_size = count;
|
|
return data;
|
|
}
|
|
|
|
static void usage(void) {
|
|
printf("Usage: bipart <min_con_value> <min_part_size> <filename>\n");
|
|
printf(" or: bipart -C <min_con_value> -M <min_part_size> <filename>\n");
|
|
}
|
|
|
|
int main(int argc, char **argv) {
|
|
int min_con_value = 0;
|
|
int min_part_size = 0;
|
|
const char *filename = NULL;
|
|
int i;
|
|
int *edges_data;
|
|
int edge_count;
|
|
SolverContext *ctx;
|
|
clock_t start;
|
|
clock_t end;
|
|
double seconds;
|
|
int *result_array;
|
|
|
|
if (argc == 4) {
|
|
min_con_value = atoi(argv[1]);
|
|
min_part_size = atoi(argv[2]);
|
|
filename = argv[3];
|
|
} else {
|
|
for (i = 1; i < argc; ++i) {
|
|
if (strcmp(argv[i], "-C") == 0 && i + 1 < argc) {
|
|
min_con_value = atoi(argv[++i]);
|
|
} else if (strcmp(argv[i], "-M") == 0 && i + 1 < argc) {
|
|
min_part_size = atoi(argv[++i]);
|
|
} else if (argv[i][0] != '-') {
|
|
filename = argv[i];
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!filename || min_con_value < 50 || min_con_value > 100 || min_part_size <= 0) {
|
|
usage();
|
|
return 1;
|
|
}
|
|
|
|
printf("Computing bi-partite components of the graph \"%s\".\n", filename);
|
|
printf("Assuming minimum connectivity %d%%.\n", min_con_value);
|
|
printf("Assuming minimum partition size %d nodes.\n\n", min_part_size);
|
|
|
|
edges_data = read_edge_list(filename, &edge_count);
|
|
|
|
start = clock();
|
|
ctx = solver_run(edges_data, edge_count, min_con_value, min_part_size);
|
|
end = clock();
|
|
seconds = (double)(end - start) / (double)CLOCKS_PER_SEC;
|
|
|
|
printf("The problem is solved in %.2f sec.\n\n", seconds);
|
|
|
|
print_components(ctx);
|
|
|
|
result_array = solver_build_result_array(ctx);
|
|
free(result_array);
|
|
|
|
solver_free_context(ctx);
|
|
free(edges_data);
|
|
return 0;
|
|
}
|
|
*/
|
|
|
|
////////////////////////////////////////////////////////////////////////
|
|
/// END OF FILE ///
|
|
////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
ABC_NAMESPACE_IMPL_END
|
|
|