]> granicus.if.org Git - postgresql/commitdiff
Avoid use of float arithmetic in bipartite_match.c.
authorTom Lane <tgl@sss.pgh.pa.us>
Sun, 23 Aug 2015 17:02:13 +0000 (13:02 -0400)
committerTom Lane <tgl@sss.pgh.pa.us>
Sun, 23 Aug 2015 17:02:18 +0000 (13:02 -0400)
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.

src/backend/lib/bipartite_match.c
src/include/lib/bipartite_match.h

index 037dd1de30b828882abd0fdc21aaf2e0a15d228f..317222f57aceaf536c13ec002f5583a51dedf59f 100644 (file)
  */
 #include "postgres.h"
 
-#include <float.h>
-#include <math.h>
 #include <limits.h>
 
 #include "lib/bipartite_match.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_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
@@ -37,26 +40,29 @@ BipartiteMatch(int u_size, int v_size, short **adjacency)
 {
        BipartiteMatchState *state = palloc(sizeof(BipartiteMatchState));
 
-       Assert(u_size < SHRT_MAX);
-       Assert(v_size < SHRT_MAX);
+       if (u_size < 0 || u_size >= SHRT_MAX ||
+               v_size < 0 || v_size >= SHRT_MAX)
+               elog(ERROR, "invalid set size for BipartiteMatch");
 
        state->u_size = u_size;
        state->v_size = v_size;
-       state->matching = 0;
        state->adjacency = adjacency;
-       state->pair_uv = palloc0((u_size + 1) * sizeof(short));
-       state->pair_vu = palloc0((v_size + 1) * sizeof(short));
-       state->distance = palloc((u_size + 1) * sizeof(float));
-       state->queue = palloc((u_size + 2) * sizeof(short));
+       state->matching = 0;
+       state->pair_uv = (short *) palloc0((u_size + 1) * sizeof(short));
+       state->pair_vu = (short *) palloc0((v_size + 1) * 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))
        {
                int                     u;
 
-               for (u = 1; u <= u_size; ++u)
+               for (u = 1; u <= u_size; u++)
+               {
                        if (state->pair_uv[u] == 0)
-                               if (hk_depth_search(state, u, 1))
+                               if (hk_depth_search(state, u))
                                        state->matching++;
+               }
 
                CHECK_FOR_INTERRUPTS(); /* just in case */
        }
@@ -79,19 +85,23 @@ BipartiteMatchFree(BipartiteMatchState *state)
        pfree(state);
 }
 
+/*
+ * Perform the breadth-first search step of H-K matching.
+ * Returns true if successful.
+ */
 static bool
 hk_breadth_search(BipartiteMatchState *state)
 {
        int                     usize = state->u_size;
        short      *queue = state->queue;
-       float      *distance = state->distance;
+       short      *distance = state->distance;
        int                     qhead = 0;              /* we never enqueue any node more than once */
        int                     qtail = 0;              /* so don't have to worry about wrapping */
        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)
                {
@@ -99,7 +109,7 @@ hk_breadth_search(BipartiteMatchState *state)
                        queue[qhead++] = u;
                }
                else
-                       distance[u] = get_float4_infinity();
+                       distance[u] = HK_INFINITY;
        }
 
        while (qtail < qhead)
@@ -111,45 +121,52 @@ hk_breadth_search(BipartiteMatchState *state)
                        short      *u_adj = state->adjacency[u];
                        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]];
 
-                               if (isinf(distance[u_next]))
+                               if (distance[u_next] == HK_INFINITY)
                                {
                                        distance[u_next] = 1 + distance[u];
+                                       Assert(qhead < usize + 2);
                                        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
-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_vu = state->pair_vu;
        short      *u_adj = state->adjacency[u];
        int                     i = u_adj ? u_adj[0] : 0;
+       short           nextdist;
 
        if (u == 0)
                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];
 
-               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_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;
 }
index 373bbede1e1b1ca112008b0b64540628ae4ab829..c106a7e41d18c400a6ca06db857deb02776ef773 100644 (file)
@@ -11,7 +11,7 @@
 /*
  * 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
- * 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
  * 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
  * the problem of planning a collection of grouping sets with the provably
  * minimal number of sort operations.
  */
-typedef struct bipartite_match_state
+typedef struct BipartiteMatchState
 {
+       /* inputs: */
        int                     u_size;                 /* size of U */
        int                     v_size;                 /* size of V */
+       short     **adjacency;          /* adjacency[u] = [k, v1,v2,v3,...,vk] */
+       /* outputs: */
        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_vu;            /* pair_vu[v] -> u */
-
-       float      *distance;           /* distance[u], float so we can have +inf */
+       /* private state for matching algorithm: */
+       short      *distance;           /* distance[u] */
        short      *queue;                      /* queue storage for breadth search */
 } 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 */