Avoid use of float arithmetic in bipartite_match.c.

Since the distances used in this algorithm are small integers (not more
than the size of the U set, in fact), there is no good reason to use float
arithmetic for them.  Use short ints instead: they're smaller, faster, and
require no special portability assumptions.

Per testing by Greg Stark, which disclosed that the code got into an
infinite loop on VAX for lack of IEEE-style float infinities.  We don't
really care all that much whether Postgres can run on a VAX anymore,
but there seems sufficient reason to change this code anyway.

In passing, make a few other small adjustments to make the code match
usual Postgres coding style a bit better.
This commit is contained in:
Tom Lane 2015-08-23 13:02:13 -04:00
parent 5956b7f9e8
commit 44ed65a545
2 changed files with 55 additions and 36 deletions

View File

@ -16,17 +16,20 @@
*/ */
#include "postgres.h" #include "postgres.h"
#include <float.h>
#include <math.h>
#include <limits.h> #include <limits.h>
#include "lib/bipartite_match.h" #include "lib/bipartite_match.h"
#include "miscadmin.h" #include "miscadmin.h"
#include "utils/builtins.h"
/*
* The distances computed in hk_breadth_search can easily be seen to never
* exceed u_size. Since we restrict u_size to be less than SHRT_MAX, we
* can therefore use SHRT_MAX as the "infinity" distance needed as a marker.
*/
#define HK_INFINITY SHRT_MAX
static bool hk_breadth_search(BipartiteMatchState *state); static bool hk_breadth_search(BipartiteMatchState *state);
static bool hk_depth_search(BipartiteMatchState *state, int u, int depth); static bool hk_depth_search(BipartiteMatchState *state, int u);
/* /*
* Given the size of U and V, where each is indexed 1..size, and an adjacency * Given the size of U and V, where each is indexed 1..size, and an adjacency
@ -37,26 +40,29 @@ BipartiteMatch(int u_size, int v_size, short **adjacency)
{ {
BipartiteMatchState *state = palloc(sizeof(BipartiteMatchState)); BipartiteMatchState *state = palloc(sizeof(BipartiteMatchState));
Assert(u_size < SHRT_MAX); if (u_size < 0 || u_size >= SHRT_MAX ||
Assert(v_size < SHRT_MAX); v_size < 0 || v_size >= SHRT_MAX)
elog(ERROR, "invalid set size for BipartiteMatch");
state->u_size = u_size; state->u_size = u_size;
state->v_size = v_size; state->v_size = v_size;
state->matching = 0;
state->adjacency = adjacency; state->adjacency = adjacency;
state->pair_uv = palloc0((u_size + 1) * sizeof(short)); state->matching = 0;
state->pair_vu = palloc0((v_size + 1) * sizeof(short)); state->pair_uv = (short *) palloc0((u_size + 1) * sizeof(short));
state->distance = palloc((u_size + 1) * sizeof(float)); state->pair_vu = (short *) palloc0((v_size + 1) * sizeof(short));
state->queue = palloc((u_size + 2) * sizeof(short)); state->distance = (short *) palloc((u_size + 1) * sizeof(short));
state->queue = (short *) palloc((u_size + 2) * sizeof(short));
while (hk_breadth_search(state)) while (hk_breadth_search(state))
{ {
int u; int u;
for (u = 1; u <= u_size; ++u) for (u = 1; u <= u_size; u++)
{
if (state->pair_uv[u] == 0) if (state->pair_uv[u] == 0)
if (hk_depth_search(state, u, 1)) if (hk_depth_search(state, u))
state->matching++; state->matching++;
}
CHECK_FOR_INTERRUPTS(); /* just in case */ CHECK_FOR_INTERRUPTS(); /* just in case */
} }
@ -79,19 +85,23 @@ BipartiteMatchFree(BipartiteMatchState *state)
pfree(state); pfree(state);
} }
/*
* Perform the breadth-first search step of H-K matching.
* Returns true if successful.
*/
static bool static bool
hk_breadth_search(BipartiteMatchState *state) hk_breadth_search(BipartiteMatchState *state)
{ {
int usize = state->u_size; int usize = state->u_size;
short *queue = state->queue; short *queue = state->queue;
float *distance = state->distance; short *distance = state->distance;
int qhead = 0; /* we never enqueue any node more than once */ int qhead = 0; /* we never enqueue any node more than once */
int qtail = 0; /* so don't have to worry about wrapping */ int qtail = 0; /* so don't have to worry about wrapping */
int u; int u;
distance[0] = get_float4_infinity(); distance[0] = HK_INFINITY;
for (u = 1; u <= usize; ++u) for (u = 1; u <= usize; u++)
{ {
if (state->pair_uv[u] == 0) if (state->pair_uv[u] == 0)
{ {
@ -99,7 +109,7 @@ hk_breadth_search(BipartiteMatchState *state)
queue[qhead++] = u; queue[qhead++] = u;
} }
else else
distance[u] = get_float4_infinity(); distance[u] = HK_INFINITY;
} }
while (qtail < qhead) while (qtail < qhead)
@ -111,45 +121,52 @@ hk_breadth_search(BipartiteMatchState *state)
short *u_adj = state->adjacency[u]; short *u_adj = state->adjacency[u];
int i = u_adj ? u_adj[0] : 0; int i = u_adj ? u_adj[0] : 0;
for (; i > 0; --i) for (; i > 0; i--)
{ {
int u_next = state->pair_vu[u_adj[i]]; int u_next = state->pair_vu[u_adj[i]];
if (isinf(distance[u_next])) if (distance[u_next] == HK_INFINITY)
{ {
distance[u_next] = 1 + distance[u]; distance[u_next] = 1 + distance[u];
Assert(qhead < usize + 2);
queue[qhead++] = u_next; queue[qhead++] = u_next;
Assert(qhead <= usize + 2);
} }
} }
} }
} }
return !isinf(distance[0]); return (distance[0] != HK_INFINITY);
} }
/*
* Perform the depth-first search step of H-K matching.
* Returns true if successful.
*/
static bool static bool
hk_depth_search(BipartiteMatchState *state, int u, int depth) hk_depth_search(BipartiteMatchState *state, int u)
{ {
float *distance = state->distance; short *distance = state->distance;
short *pair_uv = state->pair_uv; short *pair_uv = state->pair_uv;
short *pair_vu = state->pair_vu; short *pair_vu = state->pair_vu;
short *u_adj = state->adjacency[u]; short *u_adj = state->adjacency[u];
int i = u_adj ? u_adj[0] : 0; int i = u_adj ? u_adj[0] : 0;
short nextdist;
if (u == 0) if (u == 0)
return true; return true;
if (distance[u] == HK_INFINITY)
return false;
nextdist = distance[u] + 1;
if ((depth % 8) == 0) check_stack_depth();
check_stack_depth();
for (; i > 0; --i) for (; i > 0; i--)
{ {
int v = u_adj[i]; int v = u_adj[i];
if (distance[pair_vu[v]] == distance[u] + 1) if (distance[pair_vu[v]] == nextdist)
{ {
if (hk_depth_search(state, pair_vu[v], depth + 1)) if (hk_depth_search(state, pair_vu[v]))
{ {
pair_vu[v] = u; pair_vu[v] = u;
pair_uv[u] = v; pair_uv[u] = v;
@ -158,6 +175,6 @@ hk_depth_search(BipartiteMatchState *state, int u, int depth)
} }
} }
distance[u] = get_float4_infinity(); distance[u] = HK_INFINITY;
return false; return false;
} }

View File

@ -11,7 +11,7 @@
/* /*
* Given a bipartite graph consisting of nodes U numbered 1..nU, nodes V * Given a bipartite graph consisting of nodes U numbered 1..nU, nodes V
* numbered 1..nV, and an adjacency map of undirected edges in the form * numbered 1..nV, and an adjacency map of undirected edges in the form
* adjacency[u] = [n, v1, v2, v3, ... vn], we wish to find a "maximum * adjacency[u] = [k, v1, v2, v3, ... vk], we wish to find a "maximum
* cardinality matching", which is defined as follows: a matching is a subset * cardinality matching", which is defined as follows: a matching is a subset
* of the original edges such that no node has more than one edge, and a * of the original edges such that no node has more than one edge, and a
* matching has maximum cardinality if there exists no other matching with a * matching has maximum cardinality if there exists no other matching with a
@ -24,21 +24,23 @@
* the problem of planning a collection of grouping sets with the provably * the problem of planning a collection of grouping sets with the provably
* minimal number of sort operations. * minimal number of sort operations.
*/ */
typedef struct bipartite_match_state typedef struct BipartiteMatchState
{ {
/* inputs: */
int u_size; /* size of U */ int u_size; /* size of U */
int v_size; /* size of V */ int v_size; /* size of V */
short **adjacency; /* adjacency[u] = [k, v1,v2,v3,...,vk] */
/* outputs: */
int matching; /* number of edges in matching */ int matching; /* number of edges in matching */
short **adjacency; /* adjacency[u] = [n, v1,v2,v3,...,vn] */
short *pair_uv; /* pair_uv[u] -> v */ short *pair_uv; /* pair_uv[u] -> v */
short *pair_vu; /* pair_vu[v] -> u */ short *pair_vu; /* pair_vu[v] -> u */
/* private state for matching algorithm: */
float *distance; /* distance[u], float so we can have +inf */ short *distance; /* distance[u] */
short *queue; /* queue storage for breadth search */ short *queue; /* queue storage for breadth search */
} BipartiteMatchState; } BipartiteMatchState;
BipartiteMatchState *BipartiteMatch(int u_size, int v_size, short **adjacency); extern BipartiteMatchState *BipartiteMatch(int u_size, int v_size, short **adjacency);
void BipartiteMatchFree(BipartiteMatchState *state); extern void BipartiteMatchFree(BipartiteMatchState *state);
#endif /* BIPARTITE_MATCH_H */ #endif /* BIPARTITE_MATCH_H */