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

Actual source code: weights.c

      1: #include <petsc/private/matimpl.h>
      2: #include <../src/mat/impls/aij/seq/aij.h>

      4: static PetscErrorCode MatColoringCreateLexicalWeights(MatColoring mc, PetscReal *weights)
      5: {
      6:   PetscInt i, s, e;
      7:   Mat      G = mc->mat;

      9:   PetscFunctionBegin;
     10:   PetscCall(MatGetOwnershipRange(G, &s, &e));
     11:   for (i = s; i < e; i++) weights[i - s] = i;
     12:   PetscFunctionReturn(PETSC_SUCCESS);
     13: }

     15: static PetscErrorCode MatColoringCreateRandomWeights(MatColoring mc, PetscReal *weights)
     16: {
     17:   PetscInt    i, s, e;
     18:   PetscRandom rand;
     19:   PetscReal   r;
     20:   Mat         G = mc->mat;

     22:   PetscFunctionBegin;
     23:   /* each weight should be the degree plus a random perturbation */
     24:   PetscCall(PetscRandomCreate(PetscObjectComm((PetscObject)mc), &rand));
     25:   PetscCall(PetscRandomSetFromOptions(rand));
     26:   PetscCall(MatGetOwnershipRange(G, &s, &e));
     27:   for (i = s; i < e; i++) {
     28:     PetscCall(PetscRandomGetValueReal(rand, &r));
     29:     weights[i - s] = PetscAbsReal(r);
     30:   }
     31:   PetscCall(PetscRandomDestroy(&rand));
     32:   PetscFunctionReturn(PETSC_SUCCESS);
     33: }

     35: /*@
     36:   MatColoringGetDegrees - Compute the number of vertices reachable in the graph within a given distance for each locally owned row.

     38:   Collective

     40:   Input Parameters:
     41: + G        - the graph matrix
     42: - distance - the distance (in graph edges) used to define the neighborhood

     44:   Output Parameter:
     45: . degrees - array (of local size) of neighborhood sizes for each row

     47:   Level: developer

     49: .seealso: `MatColoring`, `MatColoringCreateWeights()`, `MatColoringSetWeights()`
     50: @*/
     51: PetscErrorCode MatColoringGetDegrees(Mat G, PetscInt distance, PetscInt *degrees)
     52: {
     53:   PetscInt        j, i, s, e, n, ln, lm, degree, bidx, idx, dist;
     54:   Mat             lG, *lGs;
     55:   IS              ris;
     56:   PetscInt       *seen;
     57:   const PetscInt *gidx;
     58:   PetscInt       *idxbuf;
     59:   PetscInt       *distbuf;
     60:   PetscInt        ncols;
     61:   const PetscInt *cols;
     62:   PetscBool       isSEQAIJ;
     63:   Mat_SeqAIJ     *aij;
     64:   PetscInt       *Gi, *Gj;

     66:   PetscFunctionBegin;
     67:   PetscCall(MatGetOwnershipRange(G, &s, &e));
     68:   n = e - s;
     69:   PetscCall(ISCreateStride(PetscObjectComm((PetscObject)G), n, s, 1, &ris));
     70:   PetscCall(MatIncreaseOverlap(G, 1, &ris, distance));
     71:   PetscCall(ISSort(ris));
     72:   PetscCall(MatCreateSubMatrices(G, 1, &ris, &ris, MAT_INITIAL_MATRIX, &lGs));
     73:   lG = lGs[0];
     74:   PetscCall(PetscObjectBaseTypeCompare((PetscObject)lG, MATSEQAIJ, &isSEQAIJ));
     75:   PetscCheck(isSEQAIJ, PetscObjectComm((PetscObject)G), PETSC_ERR_SUP, "Requires an MPI/SEQAIJ Matrix");
     76:   PetscCall(MatGetSize(lG, &ln, &lm));
     77:   aij = (Mat_SeqAIJ *)lG->data;
     78:   Gi  = aij->i;
     79:   Gj  = aij->j;
     80:   PetscCall(PetscMalloc3(lm, &seen, lm, &idxbuf, lm, &distbuf));
     81:   for (i = 0; i < ln; i++) seen[i] = -1;
     82:   PetscCall(ISGetIndices(ris, &gidx));
     83:   for (i = 0; i < ln; i++) {
     84:     if (gidx[i] >= e || gidx[i] < s) continue;
     85:     bidx   = -1;
     86:     ncols  = Gi[i + 1] - Gi[i];
     87:     cols   = &(Gj[Gi[i]]);
     88:     degree = 0;
     89:     /* place the distance-one neighbors on the queue */
     90:     for (j = 0; j < ncols; j++) {
     91:       bidx++;
     92:       seen[cols[j]] = i;
     93:       distbuf[bidx] = 1;
     94:       idxbuf[bidx]  = cols[j];
     95:     }
     96:     while (bidx >= 0) {
     97:       /* pop */
     98:       idx  = idxbuf[bidx];
     99:       dist = distbuf[bidx];
    100:       bidx--;
    101:       degree++;
    102:       if (dist < distance) {
    103:         ncols = Gi[idx + 1] - Gi[idx];
    104:         cols  = &(Gj[Gi[idx]]);
    105:         for (j = 0; j < ncols; j++) {
    106:           if (seen[cols[j]] != i) {
    107:             bidx++;
    108:             seen[cols[j]] = i;
    109:             idxbuf[bidx]  = cols[j];
    110:             distbuf[bidx] = dist + 1;
    111:           }
    112:         }
    113:       }
    114:     }
    115:     degrees[gidx[i] - s] = degree;
    116:   }
    117:   PetscCall(ISRestoreIndices(ris, &gidx));
    118:   PetscCall(ISDestroy(&ris));
    119:   PetscCall(PetscFree3(seen, idxbuf, distbuf));
    120:   PetscCall(MatDestroyMatrices(1, &lGs));
    121:   PetscFunctionReturn(PETSC_SUCCESS);
    122: }

    124: static PetscErrorCode MatColoringCreateLargestFirstWeights(MatColoring mc, PetscReal *weights)
    125: {
    126:   PetscInt    i, s, e, n, ncols;
    127:   PetscRandom rand;
    128:   PetscReal   r;
    129:   PetscInt   *degrees;
    130:   Mat         G = mc->mat;

    132:   PetscFunctionBegin;
    133:   /* each weight should be the degree plus a random perturbation */
    134:   PetscCall(PetscRandomCreate(PetscObjectComm((PetscObject)mc), &rand));
    135:   PetscCall(PetscRandomSetFromOptions(rand));
    136:   PetscCall(MatGetOwnershipRange(G, &s, &e));
    137:   n = e - s;
    138:   PetscCall(PetscMalloc1(n, &degrees));
    139:   PetscCall(MatColoringGetDegrees(G, mc->dist, degrees));
    140:   for (i = s; i < e; i++) {
    141:     PetscCall(MatGetRow(G, i, &ncols, NULL, NULL));
    142:     PetscCall(PetscRandomGetValueReal(rand, &r));
    143:     weights[i - s] = ncols + PetscAbsReal(r);
    144:     PetscCall(MatRestoreRow(G, i, &ncols, NULL, NULL));
    145:   }
    146:   PetscCall(PetscRandomDestroy(&rand));
    147:   PetscCall(PetscFree(degrees));
    148:   PetscFunctionReturn(PETSC_SUCCESS);
    149: }

    151: static PetscErrorCode MatColoringCreateSmallestLastWeights(MatColoring mc, PetscReal *weights)
    152: {
    153:   PetscInt       *degrees, *degb, *llprev, *llnext;
    154:   PetscInt        j, i, s, e, n, ln, lm, degree, maxdegree = 0, bidx, idx, dist, distance = mc->dist;
    155:   Mat             lG, *lGs;
    156:   IS              ris;
    157:   PetscInt       *seen;
    158:   const PetscInt *gidx;
    159:   PetscInt       *idxbuf;
    160:   PetscInt       *distbuf;
    161:   PetscInt        ncols, nxt, prv, cur;
    162:   const PetscInt *cols;
    163:   PetscBool       isSEQAIJ;
    164:   Mat_SeqAIJ     *aij;
    165:   PetscInt       *Gi, *Gj, *rperm;
    166:   Mat             G = mc->mat;
    167:   PetscReal      *lweights, r;
    168:   PetscRandom     rand;

    170:   PetscFunctionBegin;
    171:   PetscCall(MatGetOwnershipRange(G, &s, &e));
    172:   n = e - s;
    173:   PetscCall(ISCreateStride(PetscObjectComm((PetscObject)G), n, s, 1, &ris));
    174:   PetscCall(MatIncreaseOverlap(G, 1, &ris, distance + 1));
    175:   PetscCall(ISSort(ris));
    176:   PetscCall(MatCreateSubMatrices(G, 1, &ris, &ris, MAT_INITIAL_MATRIX, &lGs));
    177:   lG = lGs[0];
    178:   PetscCall(PetscObjectBaseTypeCompare((PetscObject)lG, MATSEQAIJ, &isSEQAIJ));
    179:   PetscCheck(isSEQAIJ, PetscObjectComm((PetscObject)G), PETSC_ERR_ARG_WRONGSTATE, "Requires an MPI/SEQAIJ Matrix");
    180:   PetscCall(MatGetSize(lG, &ln, &lm));
    181:   aij = (Mat_SeqAIJ *)lG->data;
    182:   Gi  = aij->i;
    183:   Gj  = aij->j;
    184:   PetscCall(PetscMalloc3(lm, &seen, lm, &idxbuf, lm, &distbuf));
    185:   PetscCall(PetscMalloc1(lm, &degrees));
    186:   PetscCall(PetscMalloc1(lm, &lweights));
    187:   for (i = 0; i < ln; i++) {
    188:     seen[i]     = -1;
    189:     lweights[i] = 1.;
    190:   }
    191:   PetscCall(ISGetIndices(ris, &gidx));
    192:   for (i = 0; i < ln; i++) {
    193:     bidx   = -1;
    194:     ncols  = Gi[i + 1] - Gi[i];
    195:     cols   = &(Gj[Gi[i]]);
    196:     degree = 0;
    197:     /* place the distance-one neighbors on the queue */
    198:     for (j = 0; j < ncols; j++) {
    199:       bidx++;
    200:       seen[cols[j]] = i;
    201:       distbuf[bidx] = 1;
    202:       idxbuf[bidx]  = cols[j];
    203:     }
    204:     while (bidx >= 0) {
    205:       /* pop */
    206:       idx  = idxbuf[bidx];
    207:       dist = distbuf[bidx];
    208:       bidx--;
    209:       degree++;
    210:       if (dist < distance) {
    211:         ncols = Gi[idx + 1] - Gi[idx];
    212:         cols  = &(Gj[Gi[idx]]);
    213:         for (j = 0; j < ncols; j++) {
    214:           if (seen[cols[j]] != i) {
    215:             bidx++;
    216:             seen[cols[j]] = i;
    217:             idxbuf[bidx]  = cols[j];
    218:             distbuf[bidx] = dist + 1;
    219:           }
    220:         }
    221:       }
    222:     }
    223:     degrees[i] = degree;
    224:     if (degree > maxdegree) maxdegree = degree;
    225:   }
    226:   /* bucket by degree by some random permutation */
    227:   PetscCall(PetscRandomCreate(PetscObjectComm((PetscObject)mc), &rand));
    228:   PetscCall(PetscRandomSetFromOptions(rand));
    229:   PetscCall(PetscMalloc1(ln, &rperm));
    230:   for (i = 0; i < ln; i++) {
    231:     PetscCall(PetscRandomGetValueReal(rand, &r));
    232:     lweights[i] = r;
    233:     rperm[i]    = i;
    234:   }
    235:   PetscCall(PetscSortRealWithPermutation(lm, lweights, rperm));
    236:   PetscCall(PetscMalloc1(maxdegree + 1, &degb));
    237:   PetscCall(PetscMalloc2(ln, &llnext, ln, &llprev));
    238:   for (i = 0; i < maxdegree + 1; i++) degb[i] = -1;
    239:   for (i = 0; i < ln; i++) {
    240:     llnext[i] = -1;
    241:     llprev[i] = -1;
    242:     seen[i]   = -1;
    243:   }
    244:   for (i = 0; i < ln; i++) {
    245:     idx         = rperm[i];
    246:     llnext[idx] = degb[degrees[idx]];
    247:     if (degb[degrees[idx]] > 0) llprev[degb[degrees[idx]]] = idx;
    248:     degb[degrees[idx]] = idx;
    249:   }
    250:   PetscCall(PetscFree(rperm));
    251:   /* remove the lowest degree one */
    252:   i = 0;
    253:   while (i != maxdegree + 1) {
    254:     for (i = 1; i < maxdegree + 1; i++) {
    255:       if (degb[i] > 0) {
    256:         cur          = degb[i];
    257:         degrees[cur] = 0;
    258:         degb[i]      = llnext[cur];
    259:         bidx         = -1;
    260:         ncols        = Gi[cur + 1] - Gi[cur];
    261:         cols         = &(Gj[Gi[cur]]);
    262:         /* place the distance-one neighbors on the queue */
    263:         for (j = 0; j < ncols; j++) {
    264:           if (cols[j] != cur) {
    265:             bidx++;
    266:             seen[cols[j]] = i;
    267:             distbuf[bidx] = 1;
    268:             idxbuf[bidx]  = cols[j];
    269:           }
    270:         }
    271:         while (bidx >= 0) {
    272:           /* pop */
    273:           idx  = idxbuf[bidx];
    274:           dist = distbuf[bidx];
    275:           bidx--;
    276:           nxt = llnext[idx];
    277:           prv = llprev[idx];
    278:           if (degrees[idx] > 0) {
    279:             /* change up the degree of the neighbors still in the graph */
    280:             if (lweights[idx] <= lweights[cur]) lweights[idx] = lweights[cur] + 1;
    281:             if (nxt > 0) llprev[nxt] = prv;
    282:             if (prv > 0) {
    283:               llnext[prv] = nxt;
    284:             } else {
    285:               degb[degrees[idx]] = nxt;
    286:             }
    287:             degrees[idx]--;
    288:             llnext[idx] = degb[degrees[idx]];
    289:             llprev[idx] = -1;
    290:             if (degb[degrees[idx]] >= 0) llprev[degb[degrees[idx]]] = idx;
    291:             degb[degrees[idx]] = idx;
    292:             if (dist < distance) {
    293:               ncols = Gi[idx + 1] - Gi[idx];
    294:               cols  = &(Gj[Gi[idx]]);
    295:               for (j = 0; j < ncols; j++) {
    296:                 if (seen[cols[j]] != i) {
    297:                   bidx++;
    298:                   seen[cols[j]] = i;
    299:                   idxbuf[bidx]  = cols[j];
    300:                   distbuf[bidx] = dist + 1;
    301:                 }
    302:               }
    303:             }
    304:           }
    305:         }
    306:         break;
    307:       }
    308:     }
    309:   }
    310:   for (i = 0; i < lm; i++) {
    311:     if (gidx[i] >= s && gidx[i] < e) weights[gidx[i] - s] = lweights[i];
    312:   }
    313:   PetscCall(PetscRandomDestroy(&rand));
    314:   PetscCall(PetscFree(degb));
    315:   PetscCall(PetscFree2(llnext, llprev));
    316:   PetscCall(PetscFree(degrees));
    317:   PetscCall(PetscFree(lweights));
    318:   PetscCall(ISRestoreIndices(ris, &gidx));
    319:   PetscCall(ISDestroy(&ris));
    320:   PetscCall(PetscFree3(seen, idxbuf, distbuf));
    321:   PetscCall(MatDestroyMatrices(1, &lGs));
    322:   PetscFunctionReturn(PETSC_SUCCESS);
    323: }

    325: /*@
    326:   MatColoringCreateWeights - Create per-row weights and, optionally, a decreasing-order permutation of them
    327:   for a `MatColoring`, using the weight scheme set on the `MatColoring`.

    329:   Collective

    331:   Input Parameter:
    332: . mc - the `MatColoring`

    334:   Output Parameters:
    335: + weights - the array of per-row weights, or `NULL` if not needed
    336: - lperm   - the permutation that sorts `weights` in decreasing order, or `NULL` if not needed

    338:   Level: developer

    340:   Note:
    341:   Both output arrays are allocated by this routine and must be freed by the caller with `PetscFree()`.

    343: .seealso: `MatColoring`, `MatColoringWeightType`, `MatColoringSetWeights()`, `MatColoringGetDegrees()`
    344: @*/
    345: PetscErrorCode MatColoringCreateWeights(MatColoring mc, PetscReal **weights, PetscInt **lperm)
    346: {
    347:   PetscInt   i, s, e, n;
    348:   PetscReal *wts;

    350:   PetscFunctionBegin;
    351:   /* create weights of the specified type */
    352:   PetscCall(MatGetOwnershipRange(mc->mat, &s, &e));
    353:   n = e - s;
    354:   PetscCall(PetscMalloc1(n, &wts));
    355:   switch (mc->weight_type) {
    356:   case MAT_COLORING_WEIGHT_RANDOM:
    357:     PetscCall(MatColoringCreateRandomWeights(mc, wts));
    358:     break;
    359:   case MAT_COLORING_WEIGHT_LEXICAL:
    360:     PetscCall(MatColoringCreateLexicalWeights(mc, wts));
    361:     break;
    362:   case MAT_COLORING_WEIGHT_LF:
    363:     PetscCall(MatColoringCreateLargestFirstWeights(mc, wts));
    364:     break;
    365:   case MAT_COLORING_WEIGHT_SL:
    366:     PetscCall(MatColoringCreateSmallestLastWeights(mc, wts));
    367:     break;
    368:   }
    369:   if (lperm) {
    370:     PetscCall(PetscMalloc1(n, lperm));
    371:     for (i = 0; i < n; i++) (*lperm)[i] = i;
    372:     PetscCall(PetscSortRealWithPermutation(n, wts, *lperm));
    373:     for (i = 0; i < n / 2; i++) {
    374:       PetscInt swp;
    375:       swp                 = (*lperm)[i];
    376:       (*lperm)[i]         = (*lperm)[n - 1 - i];
    377:       (*lperm)[n - 1 - i] = swp;
    378:     }
    379:   }
    380:   if (weights) *weights = wts;
    381:   PetscFunctionReturn(PETSC_SUCCESS);
    382: }

    384: /*@
    385:   MatColoringSetWeights - Provide user weights (and optionally a permutation ordering) for a `MatColoring`.

    387:   Collective

    389:   Input Parameters:
    390: + mc      - the `MatColoring`
    391: . weights - array of per-row weights, or `NULL` to clear any previously set weights
    392: - lperm   - the permutation sorting `weights` in decreasing order, or `NULL` to have it computed

    394:   Level: developer

    396:   Note:
    397:   The arrays are copied into the `MatColoring`; the caller retains ownership of `weights` and `lperm`.

    399: .seealso: `MatColoring`, `MatColoringCreateWeights()`, `MatColoringGetDegrees()`
    400: @*/
    401: PetscErrorCode MatColoringSetWeights(MatColoring mc, PetscReal *weights, PetscInt *lperm)
    402: {
    403:   PetscInt i, s, e, n;

    405:   PetscFunctionBegin;
    406:   PetscCall(MatGetOwnershipRange(mc->mat, &s, &e));
    407:   n = e - s;
    408:   if (weights) {
    409:     PetscCall(PetscMalloc2(n, &mc->user_weights, n, &mc->user_lperm));
    410:     for (i = 0; i < n; i++) mc->user_weights[i] = weights[i];
    411:     if (!lperm) {
    412:       for (i = 0; i < n; i++) mc->user_lperm[i] = i;
    413:       PetscCall(PetscSortRealWithPermutation(n, mc->user_weights, mc->user_lperm));
    414:       for (i = 0; i < n / 2; i++) {
    415:         PetscInt swp;
    416:         swp                       = mc->user_lperm[i];
    417:         mc->user_lperm[i]         = mc->user_lperm[n - 1 - i];
    418:         mc->user_lperm[n - 1 - i] = swp;
    419:       }
    420:     } else {
    421:       for (i = 0; i < n; i++) mc->user_lperm[i] = lperm[i];
    422:     }
    423:   } else {
    424:     mc->user_weights = NULL;
    425:     mc->user_lperm   = NULL;
    426:   }
    427:   PetscFunctionReturn(PETSC_SUCCESS);
    428: }