Coverage Report

Created: 2025-06-13 06:06

/src/postgres/src/backend/lib/bipartite_match.c
Line
Count
Source (jump to first uncovered line)
1
/*-------------------------------------------------------------------------
2
 *
3
 * bipartite_match.c
4
 *    Hopcroft-Karp maximum cardinality algorithm for bipartite graphs
5
 *
6
 * This implementation is based on pseudocode found at:
7
 *
8
 * https://en.wikipedia.org/w/index.php?title=Hopcroft%E2%80%93Karp_algorithm&oldid=593898016
9
 *
10
 * Copyright (c) 2015-2025, PostgreSQL Global Development Group
11
 *
12
 * IDENTIFICATION
13
 *    src/backend/lib/bipartite_match.c
14
 *
15
 *-------------------------------------------------------------------------
16
 */
17
#include "postgres.h"
18
19
#include <limits.h>
20
21
#include "lib/bipartite_match.h"
22
#include "miscadmin.h"
23
24
/*
25
 * The distances computed in hk_breadth_search can easily be seen to never
26
 * exceed u_size.  Since we restrict u_size to be less than SHRT_MAX, we
27
 * can therefore use SHRT_MAX as the "infinity" distance needed as a marker.
28
 */
29
0
#define HK_INFINITY  SHRT_MAX
30
31
static bool hk_breadth_search(BipartiteMatchState *state);
32
static bool hk_depth_search(BipartiteMatchState *state, int u);
33
34
/*
35
 * Given the size of U and V, where each is indexed 1..size, and an adjacency
36
 * list, perform the matching and return the resulting state.
37
 */
38
BipartiteMatchState *
39
BipartiteMatch(int u_size, int v_size, short **adjacency)
40
0
{
41
0
  BipartiteMatchState *state = palloc(sizeof(BipartiteMatchState));
42
43
0
  if (u_size < 0 || u_size >= SHRT_MAX ||
44
0
    v_size < 0 || v_size >= SHRT_MAX)
45
0
    elog(ERROR, "invalid set size for BipartiteMatch");
46
47
0
  state->u_size = u_size;
48
0
  state->v_size = v_size;
49
0
  state->adjacency = adjacency;
50
0
  state->matching = 0;
51
0
  state->pair_uv = (short *) palloc0((u_size + 1) * sizeof(short));
52
0
  state->pair_vu = (short *) palloc0((v_size + 1) * sizeof(short));
53
0
  state->distance = (short *) palloc((u_size + 1) * sizeof(short));
54
0
  state->queue = (short *) palloc((u_size + 2) * sizeof(short));
55
56
0
  while (hk_breadth_search(state))
57
0
  {
58
0
    int     u;
59
60
0
    for (u = 1; u <= u_size; u++)
61
0
    {
62
0
      if (state->pair_uv[u] == 0)
63
0
        if (hk_depth_search(state, u))
64
0
          state->matching++;
65
0
    }
66
67
0
    CHECK_FOR_INTERRUPTS(); /* just in case */
68
0
  }
69
70
0
  return state;
71
0
}
72
73
/*
74
 * Free a state returned by BipartiteMatch, except for the original adjacency
75
 * list, which is owned by the caller. This only frees memory, so it's optional.
76
 */
77
void
78
BipartiteMatchFree(BipartiteMatchState *state)
79
0
{
80
  /* adjacency matrix is treated as owned by the caller */
81
0
  pfree(state->pair_uv);
82
0
  pfree(state->pair_vu);
83
0
  pfree(state->distance);
84
0
  pfree(state->queue);
85
0
  pfree(state);
86
0
}
87
88
/*
89
 * Perform the breadth-first search step of H-K matching.
90
 * Returns true if successful.
91
 */
92
static bool
93
hk_breadth_search(BipartiteMatchState *state)
94
0
{
95
0
  int     usize = state->u_size;
96
0
  short    *queue = state->queue;
97
0
  short    *distance = state->distance;
98
0
  int     qhead = 0;    /* we never enqueue any node more than once */
99
0
  int     qtail = 0;    /* so don't have to worry about wrapping */
100
0
  int     u;
101
102
0
  distance[0] = HK_INFINITY;
103
104
0
  for (u = 1; u <= usize; u++)
105
0
  {
106
0
    if (state->pair_uv[u] == 0)
107
0
    {
108
0
      distance[u] = 0;
109
0
      queue[qhead++] = u;
110
0
    }
111
0
    else
112
0
      distance[u] = HK_INFINITY;
113
0
  }
114
115
0
  while (qtail < qhead)
116
0
  {
117
0
    u = queue[qtail++];
118
119
0
    if (distance[u] < distance[0])
120
0
    {
121
0
      short    *u_adj = state->adjacency[u];
122
0
      int     i = u_adj ? u_adj[0] : 0;
123
124
0
      for (; i > 0; i--)
125
0
      {
126
0
        int     u_next = state->pair_vu[u_adj[i]];
127
128
0
        if (distance[u_next] == HK_INFINITY)
129
0
        {
130
0
          distance[u_next] = 1 + distance[u];
131
0
          Assert(qhead < usize + 2);
132
0
          queue[qhead++] = u_next;
133
0
        }
134
0
      }
135
0
    }
136
0
  }
137
138
0
  return (distance[0] != HK_INFINITY);
139
0
}
140
141
/*
142
 * Perform the depth-first search step of H-K matching.
143
 * Returns true if successful.
144
 */
145
static bool
146
hk_depth_search(BipartiteMatchState *state, int u)
147
0
{
148
0
  short    *distance = state->distance;
149
0
  short    *pair_uv = state->pair_uv;
150
0
  short    *pair_vu = state->pair_vu;
151
0
  short    *u_adj = state->adjacency[u];
152
0
  int     i = u_adj ? u_adj[0] : 0;
153
0
  short   nextdist;
154
155
0
  if (u == 0)
156
0
    return true;
157
0
  if (distance[u] == HK_INFINITY)
158
0
    return false;
159
0
  nextdist = distance[u] + 1;
160
161
0
  check_stack_depth();
162
163
0
  for (; i > 0; i--)
164
0
  {
165
0
    int     v = u_adj[i];
166
167
0
    if (distance[pair_vu[v]] == nextdist)
168
0
    {
169
0
      if (hk_depth_search(state, pair_vu[v]))
170
0
      {
171
0
        pair_vu[v] = u;
172
0
        pair_uv[u] = v;
173
0
        return true;
174
0
      }
175
0
    }
176
0
  }
177
178
0
  distance[u] = HK_INFINITY;
179
0
  return false;
180
0
}