LCOV - code coverage report
Current view: top level - src/backend/lib - bipartite_match.c (source / functions) Coverage Total Hit
Test: PostgreSQL 19devel Lines: 97.2 % 71 69
Test Date: 2026-02-17 17:20:33 Functions: 100.0 % 4 4
Legend: Lines:     hit not hit

            Line data    Source code
       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-2026, 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              : #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          465 : BipartiteMatch(int u_size, int v_size, short **adjacency)
      40              : {
      41          465 :     BipartiteMatchState *state = palloc_object(BipartiteMatchState);
      42              : 
      43          465 :     if (u_size < 0 || u_size >= SHRT_MAX ||
      44          465 :         v_size < 0 || v_size >= SHRT_MAX)
      45            0 :         elog(ERROR, "invalid set size for BipartiteMatch");
      46              : 
      47          465 :     state->u_size = u_size;
      48          465 :     state->v_size = v_size;
      49          465 :     state->adjacency = adjacency;
      50          465 :     state->matching = 0;
      51          465 :     state->pair_uv = (short *) palloc0((u_size + 1) * sizeof(short));
      52          465 :     state->pair_vu = (short *) palloc0((v_size + 1) * sizeof(short));
      53          465 :     state->distance = (short *) palloc((u_size + 1) * sizeof(short));
      54          465 :     state->queue = (short *) palloc((u_size + 2) * sizeof(short));
      55              : 
      56         1141 :     while (hk_breadth_search(state))
      57              :     {
      58              :         int         u;
      59              : 
      60          832 :         for (u = 1; u <= u_size; u++)
      61              :         {
      62          621 :             if (state->pair_uv[u] == 0)
      63          621 :                 if (hk_depth_search(state, u))
      64          297 :                     state->matching++;
      65              :         }
      66              : 
      67          211 :         CHECK_FOR_INTERRUPTS(); /* just in case */
      68              :     }
      69              : 
      70          465 :     return state;
      71              : }
      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          465 : BipartiteMatchFree(BipartiteMatchState *state)
      79              : {
      80              :     /* adjacency matrix is treated as owned by the caller */
      81          465 :     pfree(state->pair_uv);
      82          465 :     pfree(state->pair_vu);
      83          465 :     pfree(state->distance);
      84          465 :     pfree(state->queue);
      85          465 :     pfree(state);
      86          465 : }
      87              : 
      88              : /*
      89              :  * Perform the breadth-first search step of H-K matching.
      90              :  * Returns true if successful.
      91              :  */
      92              : static bool
      93          676 : hk_breadth_search(BipartiteMatchState *state)
      94              : {
      95          676 :     int         usize = state->u_size;
      96          676 :     short      *queue = state->queue;
      97          676 :     short      *distance = state->distance;
      98          676 :     int         qhead = 0;      /* we never enqueue any node more than once */
      99          676 :     int         qtail = 0;      /* so don't have to worry about wrapping */
     100              :     int         u;
     101              : 
     102          676 :     distance[0] = HK_INFINITY;
     103              : 
     104         2375 :     for (u = 1; u <= usize; u++)
     105              :     {
     106         1699 :         if (state->pair_uv[u] == 0)
     107              :         {
     108         1402 :             distance[u] = 0;
     109         1402 :             queue[qhead++] = u;
     110              :         }
     111              :         else
     112          297 :             distance[u] = HK_INFINITY;
     113              :     }
     114              : 
     115         2303 :     while (qtail < qhead)
     116              :     {
     117         1627 :         u = queue[qtail++];
     118              : 
     119         1627 :         if (distance[u] < distance[0])
     120              :         {
     121         1416 :             short      *u_adj = state->adjacency[u];
     122         1416 :             int         i = u_adj ? u_adj[0] : 0;
     123              : 
     124         2011 :             for (; i > 0; i--)
     125              :             {
     126          595 :                 int         u_next = state->pair_vu[u_adj[i]];
     127              : 
     128          595 :                 if (distance[u_next] == HK_INFINITY)
     129              :                 {
     130          225 :                     distance[u_next] = 1 + distance[u];
     131              :                     Assert(qhead < usize + 2);
     132          225 :                     queue[qhead++] = u_next;
     133              :                 }
     134              :             }
     135              :         }
     136              :     }
     137              : 
     138          676 :     return (distance[0] != HK_INFINITY);
     139              : }
     140              : 
     141              : /*
     142              :  * Perform the depth-first search step of H-K matching.
     143              :  * Returns true if successful.
     144              :  */
     145              : static bool
     146          918 : hk_depth_search(BipartiteMatchState *state, int u)
     147              : {
     148          918 :     short      *distance = state->distance;
     149          918 :     short      *pair_uv = state->pair_uv;
     150          918 :     short      *pair_vu = state->pair_vu;
     151          918 :     short      *u_adj = state->adjacency[u];
     152          918 :     int         i = u_adj ? u_adj[0] : 0;
     153              :     short       nextdist;
     154              : 
     155          918 :     if (u == 0)
     156          297 :         return true;
     157          621 :     if (distance[u] == HK_INFINITY)
     158            0 :         return false;
     159          621 :     nextdist = distance[u] + 1;
     160              : 
     161          621 :     check_stack_depth();
     162              : 
     163          739 :     for (; i > 0; i--)
     164              :     {
     165          415 :         int         v = u_adj[i];
     166              : 
     167          415 :         if (distance[pair_vu[v]] == nextdist)
     168              :         {
     169          297 :             if (hk_depth_search(state, pair_vu[v]))
     170              :             {
     171          297 :                 pair_vu[v] = u;
     172          297 :                 pair_uv[u] = v;
     173          297 :                 return true;
     174              :             }
     175              :         }
     176              :     }
     177              : 
     178          324 :     distance[u] = HK_INFINITY;
     179          324 :     return false;
     180              : }
        

Generated by: LCOV version 2.0-1