/* * Copyright(C) 1999-2020, 2022 National Technology & Engineering Solutions * of Sandia, LLC (NTESS). Under the terms of Contract DE-NA0003525 with * NTESS, the U.S. Government retains certain rights in this software. * * See packages/seacas/LICENSE for details */ #include "defs.h" // for TRUE, FALSE #include "structs.h" // for vtx_data void median_assign(struct vtx_data **graph, /* data structure with vertex weights */ double *vals, /* values of which to find median */ int nvtxs, /* number of values I own */ double *goal, /* desired sizes for sets */ int using_vwgts, /* are vertex weights being used? */ int *sets, /* assigned set for each vertex */ double wlow, /* sum of weights below guess */ double whigh, /* sum of weights above guess */ double guess /* median value */ ) { int i; /* loop counter */ for (i = 1; i <= nvtxs; i++) { if (vals[i] < guess) { sets[i] = 0; } else if (vals[i] > guess) { sets[i] = 1; } else { if (goal[0] - wlow > goal[1] - whigh) { sets[i] = 0; if (using_vwgts) { wlow += graph[i]->vwgt; } else { wlow++; } } else { sets[i] = 1; if (using_vwgts) { whigh += graph[i]->vwgt; } else { whigh++; } } } } } /* Find the median of set of values. */ /* Can also find nested medians of several sets of values */ /* Routine works by repeatedly guessing a value, and discarding those */ /* values which are on the wrong side of the guess. */ void median(struct vtx_data **graph, /* data structure with vertex weights */ double *vals, /* values of which to find median */ int nvtxs, /* number of values I own */ int *active, /* space for list of nvtxs ints */ double *goal, /* desired sizes for sets */ int using_vwgts, /* are vertex weights being used? */ int *sets /* set each vertex gets assigned to */ ) { double *vptr; /* loops through vals array */ double val; /* value in vals array */ double maxval; /* largest active value */ double minval; /* smallest active value */ double guess = 0.0; /* approximate median value */ double nearup; /* lowest guy above guess */ double neardown; /* highest guy below guess */ double whigh; /* total weight of values above maxval */ double wlow; /* total weight of values below minval */ double wabove; /* total weight of active values above guess */ double wbelow; /* total weight of active values below guess */ double wexact; /* weight of vertices exactly at guess */ double lweight; /* desired weight of lower values in set */ double uweight; /* desired weight of upper values in set */ double frac; /* fraction of values I want less than guess */ int *aptr; /* loops through active array */ int *aptr2; /* helps update active array */ int myactive; /* number of active values I own */ double wfree; /* weight of vtxs not yet divided */ int removed; /* number of my values eliminated */ /*int npass = 0;*/ /* counts passes required to find median */ int done; /* check for termination criteria */ int vtx; /* vertex being considered */ int i; /* loop counters */ /* Initialize. */ /* Determine the desired weight sums for the two different sets. */ lweight = goal[0]; uweight = goal[1]; myactive = nvtxs; whigh = wlow = 0; /* Find largest and smallest values in vector, and construct active list. */ vptr = vals; aptr = active; minval = maxval = *(++vptr); *aptr++ = 1; for (i = 2; i <= nvtxs; i++) { *aptr++ = i; val = *(++vptr); if (val > maxval) { maxval = val; } if (val < minval) { minval = val; } } /* Loop until all sets are partitioned correctly. */ done = FALSE; while (!done) { /*npass++;*/ /* Select a potential dividing value. */ /* Currently, this assumes a linear distribution. */ wfree = lweight + uweight - (wlow + whigh); frac = (lweight - wlow) / wfree; /* Overshoot a bit to try to cut into largest set. */ frac = .5 * (frac + .5); guess = minval + frac * (maxval - minval); /* Now count the guys above and below this guess. */ /* Also find nearest values on either side of guess. */ wabove = wbelow = wexact = 0; nearup = maxval; neardown = minval; aptr = active; for (i = 0; i < myactive; i++) { vtx = *aptr++; val = vals[vtx]; if (val > guess) { if (using_vwgts) { wabove += graph[vtx]->vwgt; } else { wabove++; } if (val < nearup) { nearup = val; } } else if (val < guess) { if (using_vwgts) { wbelow += graph[vtx]->vwgt; } else { wbelow++; } if (val > neardown) { neardown = val; } } else { if (using_vwgts) { wexact += graph[vtx]->vwgt; } else { wexact++; } } } /* Select a half to discard. */ /* And remove discarded vertices from active list. */ removed = 0; if (wlow + wbelow - lweight > whigh + wabove - uweight && whigh + wabove + wexact < uweight) { /* Discard upper set. */ whigh += wabove + wexact; maxval = neardown; done = FALSE; aptr = aptr2 = active; for (i = 0; i < myactive; i++) { if (vals[*aptr] >= guess) { ++removed; } else { *aptr2++ = *aptr; } aptr++; } myactive -= removed; if (myactive == 0) { done = TRUE; } } else if (whigh + wabove - uweight > wlow + wbelow - lweight && wlow + wbelow + wexact < lweight) { /* Discard lower set. */ wlow += wbelow + wexact; minval = nearup; done = FALSE; aptr = aptr2 = active; for (i = 0; i < myactive; i++) { if (vals[*aptr] <= guess) { ++removed; } else { *aptr2++ = *aptr; } aptr++; } myactive -= removed; if (myactive == 0) { done = TRUE; } } else { /* Perfect partition! */ wlow += wbelow; whigh += wabove; done = TRUE; } /* Check for alternate termination criteria. */ if (!done && maxval == minval) { guess = maxval; done = TRUE; } } median_assign(graph, vals, nvtxs, goal, using_vwgts, sets, wlow, whigh, guess); }