Date: 2026-07-30T21:14:20+00:00

Actual source code: pheap.c

      1: #include <petsc/private/petscimpl.h>
      2: #include <petscviewer.h>

      4: typedef struct {
      5:   PetscInt id;
      6:   PetscInt value;
      7: } HeapNode;

      9: struct _n_PetscHeap {
     10:   PetscInt  end;   /* one past the last item */
     11:   PetscInt  alloc; /* length of array */
     12:   PetscInt  stash; /* stash grows down, this points to last item */
     13:   HeapNode *base;
     14: };

     16: /*
     17:   The arity of the heap can be changed via the parameter B below. Consider the B=2 (arity=4 case below)

     19:   [00 (sentinel); 01 (min node); 10 (unused); 11 (unused); 0100 (first child); 0101; 0110; 0111; ...]

     21:   Slots 10 and 11 are referred to as the "hole" below in the implementation.
     22: */

     24: #define B     1        /* log2(ARITY) */
     25: #define ARITY (1 << B) /* tree branching factor */
     26: static inline PetscInt Parent(PetscInt loc)
     27: {
     28:   PetscInt p = loc >> B;
     29:   if (p < ARITY) return (PetscInt)(loc != 1); /* Parent(1) is 0, otherwise fix entries ending up in the hole */
     30:   return p;
     31: }
     32: #define Value(h, loc) ((h)->base[loc].value)
     33: #define Id(h, loc)    ((h)->base[loc].id)

     35: static inline void Swap(PetscHeap h, PetscInt loc, PetscInt loc2)
     36: {
     37:   PetscInt id, val;
     38:   id                  = Id(h, loc);
     39:   val                 = Value(h, loc);
     40:   h->base[loc].id     = Id(h, loc2);
     41:   h->base[loc].value  = Value(h, loc2);
     42:   h->base[loc2].id    = id;
     43:   h->base[loc2].value = val;
     44: }
     45: static inline PetscInt MinChild(PetscHeap h, PetscInt loc)
     46: {
     47:   PetscInt min, chld, left, right;
     48:   left  = loc << B;
     49:   right = PetscMin(left + ARITY - 1, h->end - 1);
     50:   chld  = 0;
     51:   min   = PETSC_INT_MAX;
     52:   for (; left <= right; left++) {
     53:     PetscInt val = Value(h, left);
     54:     if (val < min) {
     55:       min  = val;
     56:       chld = left;
     57:     }
     58:   }
     59:   return chld;
     60: }

     62: /*@
     63:   PetscHeapCreate - Creates a `PetscHeap` object, a simple min-heap for `(id, value)` pairs.

     65:   Not Collective

     67:   Input Parameter:
     68: . maxsize - the maximum number of items the heap can hold at once

     70:   Output Parameter:
     71: . heap - the newly created `PetscHeap` object

     73:   Level: developer

     75:   Note:
     76:   The heap is ordered by `value`; items with equal values may be returned in any order.

     78: .seealso: `PetscHeap`, `PetscHeapAdd()`, `PetscHeapPop()`, `PetscHeapPeek()`, `PetscHeapStash()`, `PetscHeapUnstash()`, `PetscHeapView()`, `PetscHeapDestroy()`
     79: @*/
     80: PetscErrorCode PetscHeapCreate(PetscInt maxsize, PetscHeap *heap)
     81: {
     82:   PetscHeap h;

     84:   PetscFunctionBegin;
     85:   *heap = NULL;
     86:   PetscCall(PetscMalloc1(1, &h));
     87:   h->end   = 1;
     88:   h->alloc = maxsize + ARITY; /* We waste all but one slot (loc=1) in the first ARITY slots */
     89:   h->stash = h->alloc;
     90:   PetscCall(PetscCalloc1(h->alloc, &h->base));
     91:   h->base[0].id    = -1;
     92:   h->base[0].value = PETSC_INT_MIN;
     93:   *heap            = h;
     94:   PetscFunctionReturn(PETSC_SUCCESS);
     95: }

     97: /*@
     98:   PetscHeapAdd - Insert an item into a `PetscHeap`.

    100:   Not Collective

    102:   Input Parameters:
    103: + h   - the `PetscHeap`
    104: . id  - the item identifier
    105: - val - the value used for heap ordering

    107:   Level: developer

    109: .seealso: `PetscHeap`, `PetscHeapCreate()`, `PetscHeapPop()`, `PetscHeapPeek()`, `PetscHeapStash()`, `PetscHeapUnstash()`, `PetscHeapDestroy()`
    110: @*/
    111: PetscErrorCode PetscHeapAdd(PetscHeap h, PetscInt id, PetscInt val)
    112: {
    113:   PetscInt loc, par;

    115:   PetscFunctionBegin;
    116:   if (1 < h->end && h->end < ARITY) h->end = ARITY;
    117:   loc = h->end++;
    118:   PetscCheck(h->end <= h->stash, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Addition would exceed allocation %" PetscInt_FMT " (%" PetscInt_FMT " stashed)", h->alloc, h->alloc - h->stash);
    119:   h->base[loc].id    = id;
    120:   h->base[loc].value = val;

    122:   /* move up until heap condition is satisfied */
    123:   while ((void)(par = Parent(loc)), Value(h, par) > val) {
    124:     Swap(h, loc, par);
    125:     loc = par;
    126:   }
    127:   PetscFunctionReturn(PETSC_SUCCESS);
    128: }

    130: /*@
    131:   PetscHeapPop - Remove and return the minimum item from a `PetscHeap`.

    133:   Not Collective

    135:   Input Parameter:
    136: . h - the `PetscHeap`

    138:   Output Parameters:
    139: + id  - identifier of the popped item, or `-1` if the heap is empty
    140: - val - value of the popped item, or `PETSC_INT_MIN` if the heap is empty

    142:   Level: developer

    144: .seealso: `PetscHeap`, `PetscHeapCreate()`, `PetscHeapAdd()`, `PetscHeapPeek()`, `PetscHeapStash()`, `PetscHeapUnstash()`, `PetscHeapDestroy()`
    145: @*/
    146: PetscErrorCode PetscHeapPop(PetscHeap h, PetscInt *id, PetscInt *val)
    147: {
    148:   PetscInt loc, chld;

    150:   PetscFunctionBegin;
    151:   if (h->end == 1) {
    152:     *id  = h->base[0].id;
    153:     *val = h->base[0].value;
    154:     PetscFunctionReturn(PETSC_SUCCESS);
    155:   }

    157:   *id  = h->base[1].id;
    158:   *val = h->base[1].value;

    160:   /* rotate last entry into first position */
    161:   loc = --h->end;
    162:   if (h->end == ARITY) h->end = 2; /* Skip over hole */
    163:   h->base[1].id    = Id(h, loc);
    164:   h->base[1].value = Value(h, loc);

    166:   /* move down until min heap condition is satisfied */
    167:   loc = 1;
    168:   while ((chld = MinChild(h, loc)) && Value(h, loc) > Value(h, chld)) {
    169:     Swap(h, loc, chld);
    170:     loc = chld;
    171:   }
    172:   PetscFunctionReturn(PETSC_SUCCESS);
    173: }

    175: /*@
    176:   PetscHeapPeek - Return the minimum item of a `PetscHeap` without removing it.

    178:   Not Collective

    180:   Input Parameter:
    181: . h - the `PetscHeap`

    183:   Output Parameters:
    184: + id  - identifier of the minimum item, or `-1` if the heap is empty
    185: - val - value of the minimum item, or `PETSC_INT_MIN` if the heap is empty

    187:   Level: developer

    189: .seealso: `PetscHeap`, `PetscHeapCreate()`, `PetscHeapAdd()`, `PetscHeapPop()`, `PetscHeapDestroy()`
    190: @*/
    191: PetscErrorCode PetscHeapPeek(PetscHeap h, PetscInt *id, PetscInt *val)
    192: {
    193:   PetscFunctionBegin;
    194:   if (h->end == 1) {
    195:     *id  = h->base[0].id;
    196:     *val = h->base[0].value;
    197:     PetscFunctionReturn(PETSC_SUCCESS);
    198:   }

    200:   *id  = h->base[1].id;
    201:   *val = h->base[1].value;
    202:   PetscFunctionReturn(PETSC_SUCCESS);
    203: }

    205: /*@
    206:   PetscHeapStash - Set aside an item in a `PetscHeap` for later insertion via `PetscHeapUnstash()`.

    208:   Not Collective

    210:   Input Parameters:
    211: + h   - the `PetscHeap`
    212: . id  - the item identifier
    213: - val - the value used for heap ordering

    215:   Level: developer

    217:   Note:
    218:   Stashed items are held in the trailing portion of the heap's storage and do not participate in
    219:   heap ordering until `PetscHeapUnstash()` reinserts them.

    221: .seealso: `PetscHeap`, `PetscHeapCreate()`, `PetscHeapAdd()`, `PetscHeapUnstash()`, `PetscHeapDestroy()`
    222: @*/
    223: PetscErrorCode PetscHeapStash(PetscHeap h, PetscInt id, PetscInt val)
    224: {
    225:   PetscInt loc;

    227:   PetscFunctionBegin;
    228:   loc                = --h->stash;
    229:   h->base[loc].id    = id;
    230:   h->base[loc].value = val;
    231:   PetscFunctionReturn(PETSC_SUCCESS);
    232: }

    234: /*@
    235:   PetscHeapUnstash - Reinsert all items previously stashed with `PetscHeapStash()` into the heap.

    237:   Not Collective

    239:   Input Parameter:
    240: . h - the `PetscHeap`

    242:   Level: developer

    244: .seealso: `PetscHeap`, `PetscHeapCreate()`, `PetscHeapAdd()`, `PetscHeapStash()`, `PetscHeapDestroy()`
    245: @*/
    246: PetscErrorCode PetscHeapUnstash(PetscHeap h)
    247: {
    248:   PetscFunctionBegin;
    249:   while (h->stash < h->alloc) {
    250:     PetscInt id = Id(h, h->stash), value = Value(h, h->stash);
    251:     h->stash++;
    252:     PetscCall(PetscHeapAdd(h, id, value));
    253:   }
    254:   PetscFunctionReturn(PETSC_SUCCESS);
    255: }

    257: /*@
    258:   PetscHeapDestroy - Destroys a `PetscHeap` created with `PetscHeapCreate()`.

    260:   Not Collective

    262:   Input Parameter:
    263: . heap - the `PetscHeap` to destroy; set to `NULL` on return

    265:   Level: developer

    267: .seealso: `PetscHeap`, `PetscHeapCreate()`
    268: @*/
    269: PetscErrorCode PetscHeapDestroy(PetscHeap *heap)
    270: {
    271:   PetscFunctionBegin;
    272:   PetscCall(PetscFree((*heap)->base));
    273:   PetscCall(PetscFree(*heap));
    274:   PetscFunctionReturn(PETSC_SUCCESS);
    275: }

    277: /*@
    278:   PetscHeapView - View the contents of a `PetscHeap`, including any stashed items.

    280:   Not Collective

    282:   Input Parameters:
    283: + h      - the `PetscHeap`
    284: - viewer - a `PetscViewer`, or `NULL` to use `PETSC_VIEWER_STDOUT_SELF`

    286:   Level: developer

    288: .seealso: `PetscHeap`, `PetscHeapCreate()`, `PetscHeapAdd()`, `PetscHeapPop()`
    289: @*/
    290: PetscErrorCode PetscHeapView(PetscHeap h, PetscViewer viewer)
    291: {
    292:   PetscBool isascii;

    294:   PetscFunctionBegin;
    295:   if (!viewer) PetscCall(PetscViewerASCIIGetStdout(PETSC_COMM_SELF, &viewer));
    297:   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERASCII, &isascii));
    298:   if (isascii) {
    299:     PetscCall(PetscViewerASCIIPrintf(viewer, "Heap size %" PetscInt_FMT " with %" PetscInt_FMT " stashed\n", h->end - 1, h->alloc - h->stash));
    300:     PetscCall(PetscViewerASCIIPrintf(viewer, "Heap in (id,value) pairs\n"));
    301:     PetscCall(PetscIntView(2 * (h->end - 1), (const PetscInt *)(h->base + 1), viewer));
    302:     PetscCall(PetscViewerASCIIPrintf(viewer, "Stash in (id,value) pairs\n"));
    303:     PetscCall(PetscIntView(2 * (h->alloc - h->stash), (const PetscInt *)(h->base + h->stash), viewer));
    304:   }
    305:   PetscFunctionReturn(PETSC_SUCCESS);
    306: }