xref: /petsc/src/vec/is/is/utils/isdiff.c (revision 5a884c48ab0c46bab83cd9bb8710f380fa6d8bcf)
1 #include <petsc/private/isimpl.h> /*I "petscis.h"  I*/
2 #include <petsc/private/sectionimpl.h>
3 #include <petscbt.h>
4 
5 /*@
6   ISDifference - Computes the difference between two index sets.
7 
8   Collective
9 
10   Input Parameters:
11 + is1 - first index, to have items removed from it
12 - is2 - index values to be removed
13 
14   Output Parameter:
15 . isout - is1 - is2
16 
17   Level: intermediate
18 
19   Notes:
20   Negative values are removed from the lists. `is2` may have values
21   that are not in `is1`.
22 
23   This computation requires O(imax-imin) memory and O(imax-imin)
24   work, where imin and imax are the bounds on the indices in is1.
25 
26   If `is2` is `NULL`, the result is the same as for an empty `IS`, i.e., a duplicate of `is1`.
27 
28   The difference is computed separately on each MPI rank
29 
30 .seealso: [](sec_scatter), `IS`, `ISDestroy()`, `ISView()`, `ISSum()`, `ISExpand()`
31 @*/
ISDifference(IS is1,IS is2,IS * isout)32 PetscErrorCode ISDifference(IS is1, IS is2, IS *isout)
33 {
34   PetscInt        i, n1, n2, imin, imax, nout, *iout;
35   const PetscInt *i1, *i2;
36   PetscBT         mask;
37   MPI_Comm        comm;
38 
39   PetscFunctionBegin;
40   PetscValidHeaderSpecific(is1, IS_CLASSID, 1);
41   PetscAssertPointer(isout, 3);
42   if (!is2) {
43     PetscCall(ISDuplicate(is1, isout));
44     PetscFunctionReturn(PETSC_SUCCESS);
45   }
46   PetscValidHeaderSpecific(is2, IS_CLASSID, 2);
47 
48   PetscCall(ISGetIndices(is1, &i1));
49   PetscCall(ISGetLocalSize(is1, &n1));
50 
51   /* Create a bit mask array to contain required values */
52   if (n1) {
53     imin = PETSC_INT_MAX;
54     imax = 0;
55     for (i = 0; i < n1; i++) {
56       if (i1[i] < 0) continue;
57       imin = PetscMin(imin, i1[i]);
58       imax = PetscMax(imax, i1[i]);
59     }
60   } else imin = imax = 0;
61 
62   PetscCall(PetscBTCreate(imax - imin, &mask));
63   /* Put the values from is1 */
64   for (i = 0; i < n1; i++) {
65     if (i1[i] < 0) continue;
66     PetscCall(PetscBTSet(mask, i1[i] - imin));
67   }
68   PetscCall(ISRestoreIndices(is1, &i1));
69   /* Remove the values from is2 */
70   PetscCall(ISGetIndices(is2, &i2));
71   PetscCall(ISGetLocalSize(is2, &n2));
72   for (i = 0; i < n2; i++) {
73     if (i2[i] < imin || i2[i] > imax) continue;
74     PetscCall(PetscBTClear(mask, i2[i] - imin));
75   }
76   PetscCall(ISRestoreIndices(is2, &i2));
77 
78   /* Count the number in the difference */
79   nout = 0;
80   for (i = 0; i < imax - imin + 1; i++) {
81     if (PetscBTLookup(mask, i)) nout++;
82   }
83 
84   /* create the new IS containing the difference */
85   PetscCall(PetscMalloc1(nout, &iout));
86   nout = 0;
87   for (i = 0; i < imax - imin + 1; i++) {
88     if (PetscBTLookup(mask, i)) iout[nout++] = i + imin;
89   }
90   PetscCall(PetscObjectGetComm((PetscObject)is1, &comm));
91   PetscCall(ISCreateGeneral(comm, nout, iout, PETSC_OWN_POINTER, isout));
92 
93   PetscCall(PetscBTDestroy(&mask));
94   PetscFunctionReturn(PETSC_SUCCESS);
95 }
96 
97 /*@
98   ISSum - Computes the sum (union) of two index sets.
99 
100   Only sequential version (at the moment)
101 
102   Input Parameters:
103 + is1 - index set to be extended
104 - is2 - index values to be added
105 
106   Output Parameter:
107 . is3 - the sum; this can not be `is1` or `is2`
108 
109   Level: intermediate
110 
111   Notes:
112   If n1 and n2 are the sizes of the sets, this takes O(n1+n2) time;
113 
114   Both index sets need to be sorted on input.
115 
116   The sum is computed separately on each MPI rank
117 
118 .seealso: [](sec_scatter), `IS`, `ISDestroy()`, `ISView()`, `ISDifference()`, `ISExpand()`
119 @*/
ISSum(IS is1,IS is2,IS * is3)120 PetscErrorCode ISSum(IS is1, IS is2, IS *is3)
121 {
122   PetscBool       f;
123   const PetscInt *i1, *i2;
124   PetscInt        n1, n2, n3, p1, p2, *iout;
125 
126   PetscFunctionBegin;
127   PetscValidHeaderSpecific(is1, IS_CLASSID, 1);
128   PetscValidHeaderSpecific(is2, IS_CLASSID, 2);
129   PetscCheckSameComm(is1, 1, is2, 2);
130 
131   PetscCall(ISSorted(is1, &f));
132   PetscCheck(f, PETSC_COMM_SELF, PETSC_ERR_ARG_INCOMP, "Arg 1 is not sorted");
133   PetscCall(ISSorted(is2, &f));
134   PetscCheck(f, PETSC_COMM_SELF, PETSC_ERR_ARG_INCOMP, "Arg 2 is not sorted");
135 
136   PetscCall(ISGetLocalSize(is1, &n1));
137   PetscCall(ISGetLocalSize(is2, &n2));
138   if (!n2) {
139     PetscCall(ISDuplicate(is1, is3));
140     PetscFunctionReturn(PETSC_SUCCESS);
141   }
142   PetscCall(ISGetIndices(is1, &i1));
143   PetscCall(ISGetIndices(is2, &i2));
144 
145   p1 = 0;
146   p2 = 0;
147   n3 = 0;
148   do {
149     if (p1 == n1) { /* cleanup for is2 */
150       n3 += n2 - p2;
151       break;
152     } else {
153       while (p2 < n2 && i2[p2] < i1[p1]) {
154         n3++;
155         p2++;
156       }
157       if (p2 == n2) {
158         /* cleanup for is1 */
159         n3 += n1 - p1;
160         break;
161       } else {
162         if (i2[p2] == i1[p1]) {
163           n3++;
164           p1++;
165           p2++;
166         }
167       }
168     }
169     if (p2 == n2) {
170       /* cleanup for is1 */
171       n3 += n1 - p1;
172       break;
173     } else {
174       while (p1 < n1 && i1[p1] < i2[p2]) {
175         n3++;
176         p1++;
177       }
178       if (p1 == n1) {
179         /* clean up for is2 */
180         n3 += n2 - p2;
181         break;
182       } else {
183         if (i1[p1] == i2[p2]) {
184           n3++;
185           p1++;
186           p2++;
187         }
188       }
189     }
190   } while (p1 < n1 || p2 < n2);
191 
192   if (n3 == n1) { /* no new elements to be added */
193     PetscCall(ISRestoreIndices(is1, &i1));
194     PetscCall(ISRestoreIndices(is2, &i2));
195     PetscCall(ISDuplicate(is1, is3));
196     PetscFunctionReturn(PETSC_SUCCESS);
197   }
198   PetscCall(PetscMalloc1(n3, &iout));
199 
200   p1 = 0;
201   p2 = 0;
202   n3 = 0;
203   do {
204     if (p1 == n1) { /* cleanup for is2 */
205       while (p2 < n2) iout[n3++] = i2[p2++];
206       break;
207     } else {
208       while (p2 < n2 && i2[p2] < i1[p1]) iout[n3++] = i2[p2++];
209       if (p2 == n2) { /* cleanup for is1 */
210         while (p1 < n1) iout[n3++] = i1[p1++];
211         break;
212       } else {
213         if (i2[p2] == i1[p1]) {
214           iout[n3++] = i1[p1++];
215           p2++;
216         }
217       }
218     }
219     if (p2 == n2) { /* cleanup for is1 */
220       while (p1 < n1) iout[n3++] = i1[p1++];
221       break;
222     } else {
223       while (p1 < n1 && i1[p1] < i2[p2]) iout[n3++] = i1[p1++];
224       if (p1 == n1) { /* clean up for is2 */
225         while (p2 < n2) iout[n3++] = i2[p2++];
226         break;
227       } else {
228         if (i1[p1] == i2[p2]) {
229           iout[n3++] = i1[p1++];
230           p2++;
231         }
232       }
233     }
234   } while (p1 < n1 || p2 < n2);
235 
236   PetscCall(ISRestoreIndices(is1, &i1));
237   PetscCall(ISRestoreIndices(is2, &i2));
238   PetscCall(ISCreateGeneral(PetscObjectComm((PetscObject)is1), n3, iout, PETSC_OWN_POINTER, is3));
239   PetscFunctionReturn(PETSC_SUCCESS);
240 }
241 
242 /*@
243   ISExpand - Computes the union of two index sets, by concatenating 2 lists and
244   removing duplicates.
245 
246   Collective
247 
248   Input Parameters:
249 + is1 - first index set
250 - is2 - index values to be added
251 
252   Output Parameter:
253 . isout - `is1` + `is2` The index set `is2` is appended to `is1` removing duplicates
254 
255   Level: intermediate
256 
257   Notes:
258   Negative values are removed from the lists. This requires O(imax-imin)
259   memory and O(imax-imin) work, where imin and imax are the bounds on the
260   indices in `is1` and `is2`.
261 
262   `is1` and `is2` do not need to be sorted.
263 
264   The operations are performed separately on each MPI rank
265 
266 .seealso: [](sec_scatter), `IS`, `ISDestroy()`, `ISView()`, `ISDifference()`, `ISSum()`, `ISIntersect()`
267 @*/
ISExpand(IS is1,IS is2,IS * isout)268 PetscErrorCode ISExpand(IS is1, IS is2, IS *isout)
269 {
270   PetscInt        i, n1, n2, imin, imax, nout, *iout;
271   const PetscInt *i1, *i2;
272   PetscBool       sorted1 = PETSC_TRUE, sorted2 = PETSC_TRUE;
273   PetscBT         mask;
274   MPI_Comm        comm;
275 
276   PetscFunctionBegin;
277   if (is1) PetscValidHeaderSpecific(is1, IS_CLASSID, 1);
278   if (is2) PetscValidHeaderSpecific(is2, IS_CLASSID, 2);
279   PetscAssertPointer(isout, 3);
280 
281   PetscCheck(is1 || is2, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Both arguments cannot be NULL");
282   if (!is1) {
283     PetscCall(ISDuplicate(is2, isout));
284     PetscFunctionReturn(PETSC_SUCCESS);
285   }
286   if (!is2) {
287     PetscCall(ISDuplicate(is1, isout));
288     PetscFunctionReturn(PETSC_SUCCESS);
289   }
290   PetscCall(ISGetIndices(is1, &i1));
291   PetscCall(ISGetLocalSize(is1, &n1));
292   PetscCall(ISGetIndices(is2, &i2));
293   PetscCall(ISGetLocalSize(is2, &n2));
294 
295   /* Create a bit mask array to contain required values */
296   if (n1 || n2) {
297     imin = PETSC_INT_MAX;
298     imax = 0;
299     if (n1) {
300       PetscCall(ISSorted(is1, &sorted1));
301       if (sorted1 && i1[0] >= 0) imin = i1[0], imax = i1[n1 - 1];
302       else
303         for (i = 0; i < n1; i++) {
304           if (i1[i] < 0) continue;
305           imin = PetscMin(imin, i1[i]);
306           imax = PetscMax(imax, i1[i]);
307         }
308     }
309     if (n2) {
310       PetscCall(ISSorted(is2, &sorted2));
311       if (sorted2 && i2[0] >= 0) imin = PetscMin(imin, i2[0]), imax = PetscMax(imax, i2[n2 - 1]);
312       else
313         for (i = 0; i < n2; i++) {
314           if (i2[i] < 0) continue;
315           imin = PetscMin(imin, i2[i]);
316           imax = PetscMax(imax, i2[i]);
317         }
318     }
319   } else imin = imax = 0;
320 
321   PetscCall(PetscMalloc1(n1 + n2, &iout));
322   nout = 0;
323   PetscCall(PetscBTCreate(imax - imin, &mask));
324   /* Put the values from is1 */
325   for (i = 0; i < n1; i++) {
326     if (i1[i] < 0) continue;
327     if (!PetscBTLookupSet(mask, i1[i] - imin)) iout[nout++] = i1[i];
328   }
329   n1 = -nout;
330   PetscCall(ISRestoreIndices(is1, &i1));
331   /* Put the values from is2 */
332   for (i = 0; i < n2; i++) {
333     if (i2[i] < 0) continue;
334     if (!PetscBTLookupSet(mask, i2[i] - imin)) iout[nout++] = i2[i];
335   }
336   PetscCall(ISRestoreIndices(is2, &i2));
337 
338   /* create the new IS containing the sum */
339   PetscCall(PetscObjectGetComm((PetscObject)is1, &comm));
340   PetscCall(ISCreateGeneral(comm, nout, iout, PETSC_OWN_POINTER, isout));
341   /* no entries of is2 (resp. is1) was inserted, so if is1 (resp. is2) is sorted, then so is isout */
342   if ((-n1 == nout && sorted1) || (n1 == 0 && sorted2)) PetscCall(ISSetInfo(*isout, IS_SORTED, IS_LOCAL, PETSC_FALSE, PETSC_TRUE));
343   PetscCall(PetscBTDestroy(&mask));
344   PetscFunctionReturn(PETSC_SUCCESS);
345 }
346 
347 /*@
348   ISIntersect - Computes the intersection of two index sets, by sorting and comparing.
349 
350   Collective
351 
352   Input Parameters:
353 + is1 - first index set
354 - is2 - second index set
355 
356   Output Parameter:
357 . isout - the sorted intersection of `is1` and `is2`
358 
359   Level: intermediate
360 
361   Notes:
362   Negative values are removed from the lists. This requires O(min(is1,is2))
363   memory and O(max(is1,is2)log(max(is1,is2))) work
364 
365   `is1` and `is2` do not need to be sorted.
366 
367   The operations are performed separately on each MPI rank
368 
369 .seealso: [](sec_scatter), `IS`, `ISDestroy()`, `ISView()`, `ISDifference()`, `ISSum()`, `ISExpand()`, `ISConcatenate()`
370 @*/
ISIntersect(IS is1,IS is2,IS * isout)371 PetscErrorCode ISIntersect(IS is1, IS is2, IS *isout)
372 {
373   PetscInt        i, n1, n2, nout, *iout;
374   const PetscInt *i1, *i2;
375   IS              is1sorted = NULL, is2sorted = NULL;
376   PetscBool       sorted, lsorted;
377   MPI_Comm        comm;
378 
379   PetscFunctionBegin;
380   PetscValidHeaderSpecific(is1, IS_CLASSID, 1);
381   PetscValidHeaderSpecific(is2, IS_CLASSID, 2);
382   PetscCheckSameComm(is1, 1, is2, 2);
383   PetscAssertPointer(isout, 3);
384   PetscCall(PetscObjectGetComm((PetscObject)is1, &comm));
385 
386   PetscCall(ISGetLocalSize(is1, &n1));
387   PetscCall(ISGetLocalSize(is2, &n2));
388   if (n1 < n2) {
389     IS       tempis = is1;
390     PetscInt ntemp  = n1;
391 
392     is1 = is2;
393     is2 = tempis;
394     n1  = n2;
395     n2  = ntemp;
396   }
397   PetscCall(ISSorted(is1, &lsorted));
398   PetscCallMPI(MPIU_Allreduce(&lsorted, &sorted, 1, MPI_C_BOOL, MPI_LAND, comm));
399   if (!sorted) {
400     PetscCall(ISDuplicate(is1, &is1sorted));
401     PetscCall(ISSort(is1sorted));
402     PetscCall(ISGetIndices(is1sorted, &i1));
403   } else {
404     is1sorted = is1;
405     PetscCall(PetscObjectReference((PetscObject)is1));
406     PetscCall(ISGetIndices(is1, &i1));
407   }
408   PetscCall(ISSorted(is2, &lsorted));
409   PetscCallMPI(MPIU_Allreduce(&lsorted, &sorted, 1, MPI_C_BOOL, MPI_LAND, comm));
410   if (!sorted) {
411     PetscCall(ISDuplicate(is2, &is2sorted));
412     PetscCall(ISSort(is2sorted));
413     PetscCall(ISGetIndices(is2sorted, &i2));
414   } else {
415     is2sorted = is2;
416     PetscCall(PetscObjectReference((PetscObject)is2));
417     PetscCall(ISGetIndices(is2, &i2));
418   }
419 
420   PetscCall(PetscMalloc1(n2, &iout));
421 
422   for (i = 0, nout = 0; i < n2; i++) {
423     PetscInt key = i2[i];
424     PetscInt loc;
425 
426     PetscCall(ISLocate(is1sorted, key, &loc));
427     if (loc >= 0) {
428       if (!nout || iout[nout - 1] < key) iout[nout++] = key;
429     }
430   }
431   PetscCall(PetscRealloc(nout * sizeof(PetscInt), &iout));
432 
433   /* create the new IS containing the sum */
434   PetscCall(ISCreateGeneral(comm, nout, iout, PETSC_OWN_POINTER, isout));
435   PetscCall(ISSetInfo(*isout, IS_SORTED, IS_GLOBAL, PETSC_FALSE, PETSC_TRUE));
436   PetscCall(ISRestoreIndices(is2sorted, &i2));
437   PetscCall(ISDestroy(&is2sorted));
438   PetscCall(ISRestoreIndices(is1sorted, &i1));
439   PetscCall(ISDestroy(&is1sorted));
440   PetscFunctionReturn(PETSC_SUCCESS);
441 }
442 
ISIntersect_Caching_Internal(IS is1,IS is2,IS * isect)443 PetscErrorCode ISIntersect_Caching_Internal(IS is1, IS is2, IS *isect)
444 {
445   PetscFunctionBegin;
446   *isect = NULL;
447   if (is2 && is1) {
448     char          composeStr[33] = {0};
449     PetscObjectId is2id;
450 
451     PetscCall(PetscObjectGetId((PetscObject)is2, &is2id));
452     PetscCall(PetscSNPrintf(composeStr, 32, "ISIntersect_Caching_%" PetscInt64_FMT, is2id));
453     PetscCall(PetscObjectQuery((PetscObject)is1, composeStr, (PetscObject *)isect));
454     if (*isect == NULL) {
455       PetscCall(ISIntersect(is1, is2, isect));
456       PetscCall(PetscObjectCompose((PetscObject)is1, composeStr, (PetscObject)*isect));
457     } else {
458       PetscCall(PetscObjectReference((PetscObject)*isect));
459     }
460   }
461   PetscFunctionReturn(PETSC_SUCCESS);
462 }
463 
464 /*@
465   ISConcatenate - Forms a new `IS` by locally concatenating the indices from an `IS` list without reordering.
466 
467   Collective
468 
469   Input Parameters:
470 + comm   - communicator of the concatenated `IS`.
471 . len    - size of islist array (nonnegative)
472 - islist - array of index sets
473 
474   Output Parameter:
475 . isout - The concatenated index set; empty, if `len` == 0.
476 
477   Level: intermediate
478 
479   Notes:
480   The semantics of calling this on comm imply that the comms of the members of `islist` also contain this rank.
481 
482 .seealso: [](sec_scatter), `IS`, `ISDifference()`, `ISSum()`, `ISExpand()`, `ISIntersect()`
483 @*/
ISConcatenate(MPI_Comm comm,PetscInt len,const IS islist[],IS * isout)484 PetscErrorCode ISConcatenate(MPI_Comm comm, PetscInt len, const IS islist[], IS *isout)
485 {
486   PetscInt        i, n, N;
487   const PetscInt *iidx;
488   PetscInt       *idx;
489 
490   PetscFunctionBegin;
491   if (len) PetscAssertPointer(islist, 3);
492   if (PetscDefined(USE_DEBUG)) {
493     for (i = 0; i < len; ++i)
494       if (islist[i]) PetscValidHeaderSpecific(islist[i], IS_CLASSID, 3);
495   }
496   PetscAssertPointer(isout, 4);
497   if (!len) {
498     PetscCall(ISCreateGeneral(comm, 0, NULL, PETSC_OWN_POINTER, isout));
499     PetscFunctionReturn(PETSC_SUCCESS);
500   }
501   PetscCheck(len >= 0, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Negative array length: %" PetscInt_FMT, len);
502   N = 0;
503   for (i = 0; i < len; ++i) {
504     if (islist[i]) {
505       PetscCall(ISGetLocalSize(islist[i], &n));
506       N += n;
507     }
508   }
509   PetscCall(PetscMalloc1(N, &idx));
510   N = 0;
511   for (i = 0; i < len; ++i) {
512     if (islist[i]) {
513       PetscCall(ISGetLocalSize(islist[i], &n));
514       if (n) {
515         PetscCall(ISGetIndices(islist[i], &iidx));
516         PetscCall(PetscArraycpy(idx + N, iidx, n));
517         PetscCall(ISRestoreIndices(islist[i], &iidx));
518         N += n;
519       }
520     }
521   }
522   PetscCall(ISCreateGeneral(comm, N, idx, PETSC_OWN_POINTER, isout));
523   PetscFunctionReturn(PETSC_SUCCESS);
524 }
525 
526 /*@
527   ISListToPair  - Convert an `IS` list to a pair of `IS` of equal length defining an equivalent integer multimap.
528   Each `IS` in `islist` is assigned an integer j so that all of the indices of that `IS` are
529   mapped to j.
530 
531   Collective
532 
533   Input Parameters:
534 + comm    - `MPI_Comm`
535 . listlen - `IS` list length
536 - islist  - `IS` list
537 
538   Output Parameters:
539 + xis - domain `IS`
540 - yis - range  `IS`
541 
542   Level: developer
543 
544   Notes:
545   The global integers assigned to the `IS` of `islist` might not correspond to the
546   local numbers of the `IS` on that list, but the two *orderings* are the same: the global
547   integers assigned to the `IS` on the local list form a strictly increasing sequence.
548 
549   The `IS` in `islist` can belong to subcommunicators of `comm`, and the subcommunicators
550   on the input `IS` list are assumed to be in a "deadlock-free" order.
551 
552   Local lists of `PetscObject`s (or their subcomms) on a comm are "deadlock-free" if subcomm1
553   precedes subcomm2 on any local list, then it precedes subcomm2 on all ranks.
554   Equivalently, the local numbers of the subcomms on each local list are drawn from some global
555   numbering. This is ensured, for example, by `ISPairToList()`.
556 
557 .seealso: `IS`, `ISPairToList()`
558 @*/
ISListToPair(MPI_Comm comm,PetscInt listlen,IS islist[],IS * xis,IS * yis)559 PetscErrorCode ISListToPair(MPI_Comm comm, PetscInt listlen, IS islist[], IS *xis, IS *yis)
560 {
561   PetscInt        ncolors, *colors, leni, len, *xinds, *yinds, k;
562   const PetscInt *indsi;
563 
564   PetscFunctionBegin;
565   PetscCall(PetscMalloc1(listlen, &colors));
566   PetscCall(PetscObjectsListGetGlobalNumbering(comm, listlen, (PetscObject *)islist, &ncolors, colors));
567   len = 0;
568   for (PetscInt i = 0; i < listlen; ++i) {
569     PetscCall(ISGetLocalSize(islist[i], &leni));
570     len += leni;
571   }
572   PetscCall(PetscMalloc1(len, &xinds));
573   PetscCall(PetscMalloc1(len, &yinds));
574   k = 0;
575   for (PetscInt i = 0; i < listlen; ++i) {
576     PetscCall(ISGetLocalSize(islist[i], &leni));
577     PetscCall(ISGetIndices(islist[i], &indsi));
578     for (PetscInt j = 0; j < leni; ++j) {
579       xinds[k] = indsi[j];
580       yinds[k] = colors[i];
581       ++k;
582     }
583   }
584   PetscCall(PetscFree(colors));
585   PetscCall(ISCreateGeneral(comm, len, xinds, PETSC_OWN_POINTER, xis));
586   PetscCall(ISCreateGeneral(comm, len, yinds, PETSC_OWN_POINTER, yis));
587   PetscFunctionReturn(PETSC_SUCCESS);
588 }
589 
590 /*@C
591   ISPairToList - Convert an `IS` pair encoding an integer map to a list of `IS`.
592 
593   Collective
594 
595   Input Parameters:
596 + xis - domain `IS`
597 - yis - range `IS`, the maximum value must be less than `PETSC_MPI_INT_MAX`
598 
599   Output Parameters:
600 + listlen - length of `islist`
601 - islist  - list of `IS`s breaking up indices by color
602 
603   Level: developer
604 
605   Notes:
606   Each `IS` in `islist` contains the preimage for each index on `yis`.
607   The `IS` in `islist` are constructed on the subcommunicators of the input `IS`
608   pair. Each subcommunicator corresponds to the preimage of some index j -- this subcomm
609   contains exactly the MPI processes that assign some indices i to j.  This is essentially the inverse
610   of `ISListToPair()`.
611 
612   `xis` and `yis` must be of the same length and have congruent communicators.
613 
614   The resulting `IS` have subcommunicators in a "deadlock-free" order (see `ISListToPair()`).
615 
616 .seealso: `IS`, `ISListToPair()`
617  @*/
ISPairToList(IS xis,IS yis,PetscInt * listlen,IS ** islist)618 PetscErrorCode ISPairToList(IS xis, IS yis, PetscInt *listlen, IS **islist)
619 {
620   IS              indis = xis, coloris = yis;
621   PetscInt       *inds, *colors, llen, ilen, lstart, lend, lcount, l;
622   PetscMPIInt     rank, size, llow, lhigh, low, high, color, subsize;
623   const PetscInt *ccolors, *cinds;
624   MPI_Comm        comm, subcomm;
625 
626   PetscFunctionBegin;
627   PetscValidHeaderSpecific(xis, IS_CLASSID, 1);
628   PetscValidHeaderSpecific(yis, IS_CLASSID, 2);
629   PetscCheckSameComm(xis, 1, yis, 2);
630   PetscAssertPointer(listlen, 3);
631   PetscAssertPointer(islist, 4);
632   PetscCall(PetscObjectGetComm((PetscObject)xis, &comm));
633   PetscCallMPI(MPI_Comm_rank(comm, &rank));
634   PetscCallMPI(MPI_Comm_size(comm, &size));
635   /* Extract, copy and sort the local indices and colors on the color. */
636   PetscCall(ISGetLocalSize(coloris, &llen));
637   PetscCall(ISGetLocalSize(indis, &ilen));
638   PetscCheck(llen == ilen, comm, PETSC_ERR_ARG_SIZ, "Incompatible IS sizes: %" PetscInt_FMT " and %" PetscInt_FMT, ilen, llen);
639   PetscCall(ISGetIndices(coloris, &ccolors));
640   PetscCall(ISGetIndices(indis, &cinds));
641   PetscCall(PetscMalloc2(ilen, &inds, llen, &colors));
642   PetscCall(PetscArraycpy(inds, cinds, ilen));
643   PetscCall(PetscArraycpy(colors, ccolors, llen));
644   PetscCall(PetscSortIntWithArray(llen, colors, inds));
645   /* Determine the global extent of colors. */
646   llow   = 0;
647   lhigh  = -1;
648   lstart = 0;
649   lcount = 0;
650   while (lstart < llen) {
651     lend = lstart + 1;
652     while (lend < llen && colors[lend] == colors[lstart]) ++lend;
653     PetscCall(PetscMPIIntCast(PetscMin(llow, colors[lstart]), &llow));
654     PetscCall(PetscMPIIntCast(PetscMax(lhigh, colors[lstart]), &lhigh));
655     ++lcount;
656   }
657   PetscCallMPI(MPIU_Allreduce(&llow, &low, 1, MPI_INT, MPI_MIN, comm));
658   PetscCallMPI(MPIU_Allreduce(&lhigh, &high, 1, MPI_INT, MPI_MAX, comm));
659   *listlen = 0;
660   if (low <= high) {
661     if (lcount > 0) {
662       *listlen = lcount;
663       if (!*islist) PetscCall(PetscMalloc1(lcount, islist));
664     }
665     /*
666      Traverse all possible global colors, and participate in the subcommunicators
667      for the locally-supported colors.
668      */
669     lcount = 0;
670     lstart = 0;
671     lend   = 0;
672     for (l = low; l <= high; ++l) {
673       /*
674        Find the range of indices with the same color, which is not smaller than l.
675        Observe that, since colors is sorted, and is a subsequence of [low,high],
676        as soon as we find a new color, it is >= l.
677        */
678       if (lstart < llen) {
679         /* The start of the next locally-owned color is identified.  Now look for the end. */
680         if (lstart == lend) {
681           lend = lstart + 1;
682           while (lend < llen && colors[lend] == colors[lstart]) ++lend;
683         }
684         /* Now check whether the identified color segment matches l. */
685         PetscCheck(colors[lstart] >= l, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Locally owned color %" PetscInt_FMT " at location %" PetscInt_FMT " is < than the next global color %" PetscInt_FMT, colors[lstart], lcount, l);
686       }
687       color = (PetscMPIInt)(colors[lstart] == l);
688       /* Check whether a proper subcommunicator exists. */
689       PetscCallMPI(MPIU_Allreduce(&color, &subsize, 1, MPI_INT, MPI_SUM, comm));
690 
691       if (subsize == 1) subcomm = PETSC_COMM_SELF;
692       else if (subsize == size) subcomm = comm;
693       else {
694         /* a proper communicator is necessary, so we create it. */
695         PetscCallMPI(MPI_Comm_split(comm, color, rank, &subcomm));
696       }
697       if (colors[lstart] == l) {
698         /* If we have l among the local colors, we create an IS to hold the corresponding indices. */
699         PetscCall(ISCreateGeneral(subcomm, lend - lstart, inds + lstart, PETSC_COPY_VALUES, *islist + lcount));
700         /* Position lstart at the beginning of the next local color. */
701         lstart = lend;
702         /* Increment the counter of the local colors split off into an IS. */
703         ++lcount;
704       }
705       if (subsize > 0 && subsize < size) {
706         /*
707          Irrespective of color, destroy the split off subcomm:
708          a subcomm used in the IS creation above is duplicated
709          into a proper PETSc comm.
710          */
711         PetscCallMPI(MPI_Comm_free(&subcomm));
712       }
713     } /* for (l = low; l < high; ++l) */
714   } /* if (low <= high) */
715   PetscCall(PetscFree2(inds, colors));
716   PetscFunctionReturn(PETSC_SUCCESS);
717 }
718 
719 /*@
720   ISEmbed - Embed `IS` `a` into `IS` `b` by finding the locations in `b` that have the same indices as in `a`.
721   If `c` is the `IS` of these locations, we have `a = b*c`, regarded as a composition of the
722   corresponding `ISLocalToGlobalMapping`.
723 
724   Not Collective
725 
726   Input Parameters:
727 + a    - `IS` to embed
728 . b    - `IS` to embed into
729 - drop - flag indicating whether to drop indices of `a` that are not in `b`.
730 
731   Output Parameter:
732 . c - local embedding indices
733 
734   Level: developer
735 
736   Notes:
737   If some of the global indices of `a` are not among the indices of `b`, the embedding is impossible. The local indices of `a`
738   corresponding to these global indices are either mapped to -1 (if `!drop`) or are omitted (if `drop`). In the former
739   case the size of `c` is the same as that of `a`, in the latter case the size of `c` may be smaller.
740 
741   The resulting `IS` is sequential, since the index substitution it encodes is purely local.
742 
743 .seealso: `IS`, `ISLocalToGlobalMapping`
744  @*/
ISEmbed(IS a,IS b,PetscBool drop,IS * c)745 PetscErrorCode ISEmbed(IS a, IS b, PetscBool drop, IS *c)
746 {
747   ISLocalToGlobalMapping     ltog;
748   ISGlobalToLocalMappingMode gtoltype = IS_GTOLM_DROP;
749   PetscInt                   alen, clen, *cindices, *cindices2;
750   const PetscInt            *aindices;
751 
752   PetscFunctionBegin;
753   PetscValidHeaderSpecific(a, IS_CLASSID, 1);
754   PetscValidHeaderSpecific(b, IS_CLASSID, 2);
755   PetscAssertPointer(c, 4);
756   PetscCall(ISLocalToGlobalMappingCreateIS(b, &ltog));
757   PetscCall(ISGetLocalSize(a, &alen));
758   PetscCall(ISGetIndices(a, &aindices));
759   PetscCall(PetscMalloc1(alen, &cindices));
760   if (!drop) gtoltype = IS_GTOLM_MASK;
761   PetscCall(ISGlobalToLocalMappingApply(ltog, gtoltype, alen, aindices, &clen, cindices));
762   PetscCall(ISRestoreIndices(a, &aindices));
763   PetscCall(ISLocalToGlobalMappingDestroy(&ltog));
764   if (clen != alen) {
765     cindices2 = cindices;
766     PetscCall(PetscMalloc1(clen, &cindices));
767     PetscCall(PetscArraycpy(cindices, cindices2, clen));
768     PetscCall(PetscFree(cindices2));
769   }
770   PetscCall(ISCreateGeneral(PETSC_COMM_SELF, clen, cindices, PETSC_OWN_POINTER, c));
771   PetscFunctionReturn(PETSC_SUCCESS);
772 }
773 
774 /*@
775   ISSortPermutation  -  calculate the permutation of the indices into a nondecreasing order.
776 
777   Not Collective
778 
779   Input Parameters:
780 + f      - `IS` to sort
781 - always - build the permutation even when `f`'s indices are nondecreasing.
782 
783   Output Parameter:
784 . h - permutation or `NULL`, if `f` is nondecreasing and `always` == `PETSC_FALSE`.
785 
786   Level: advanced
787 
788   Notes:
789   Indices in `f` are unchanged. f[h[i]] is the i-th smallest f index.
790 
791   If always == `PETSC_FALSE`, an extra check is performed to see whether
792   the `f` indices are nondecreasing. `h` is built on `PETSC_COMM_SELF`, since
793   the permutation has a local meaning only.
794 
795 .seealso: `IS`, `ISLocalToGlobalMapping`, `ISSort()`
796  @*/
ISSortPermutation(IS f,PetscBool always,IS * h)797 PetscErrorCode ISSortPermutation(IS f, PetscBool always, IS *h)
798 {
799   const PetscInt *findices;
800   PetscInt        fsize, *hindices, i;
801   PetscBool       isincreasing;
802 
803   PetscFunctionBegin;
804   PetscValidHeaderSpecific(f, IS_CLASSID, 1);
805   PetscAssertPointer(h, 3);
806   PetscCall(ISGetLocalSize(f, &fsize));
807   PetscCall(ISGetIndices(f, &findices));
808   *h = NULL;
809   if (!always) {
810     isincreasing = PETSC_TRUE;
811     for (i = 1; i < fsize; ++i) {
812       if (findices[i] <= findices[i - 1]) {
813         isincreasing = PETSC_FALSE;
814         break;
815       }
816     }
817     if (isincreasing) {
818       PetscCall(ISRestoreIndices(f, &findices));
819       PetscFunctionReturn(PETSC_SUCCESS);
820     }
821   }
822   PetscCall(PetscMalloc1(fsize, &hindices));
823   for (i = 0; i < fsize; ++i) hindices[i] = i;
824   PetscCall(PetscSortIntWithPermutation(fsize, findices, hindices));
825   PetscCall(ISRestoreIndices(f, &findices));
826   PetscCall(ISCreateGeneral(PETSC_COMM_SELF, fsize, hindices, PETSC_OWN_POINTER, h));
827   PetscCall(ISSetInfo(*h, IS_PERMUTATION, IS_LOCAL, PETSC_FALSE, PETSC_TRUE));
828   PetscFunctionReturn(PETSC_SUCCESS);
829 }
830