diff options
Diffstat (limited to 'src/divonne/Split.c')
-rw-r--r-- | src/divonne/Split.c | 311 |
1 files changed, 311 insertions, 0 deletions
diff --git a/src/divonne/Split.c b/src/divonne/Split.c new file mode 100644 index 0000000..e5883e6 --- /dev/null +++ b/src/divonne/Split.c @@ -0,0 +1,311 @@ +/* + Split.c + determine optimal cuts for splitting a region + this file is part of Divonne + last modified 15 Nov 11 th +*/ + + +#define BNDTOL .05 +#define FRACT .5 +#define BIG 1e10 +#define SINGTOL 1e-4 + +#define LHSTOL .1 +#define GAMMATOL .1 + +/* the next four macros must be in sync with the typedef of Bounds! */ +#define Lower(d) (2*d) +#define Upper(d) (2*d + 1) +#define Dim(i) ((i) >> 1) +#define SignedDelta(i) (2*(i & 1) - 1)*delta[i] + +typedef struct { + count i; + real save, delta; + real f, df, fold; + real lhs, row, sol; +} Cut; + +/*********************************************************************/ + +static inline real Div(creal a, creal b) +{ + return (b != 0 && fabs(b) < BIG*fabs(a)) ? a/b : a; +} + +/*********************************************************************/ + +static void SomeCut(This *t, Cut *cut, Bounds *b) +{ + count dim, maxdim; + static count nextdim = 0; + real xmid[NDIM], ymid, maxdev; + + for( dim = 0; dim < t->ndim; ++dim ) + xmid[dim] = .5*(b[dim].upper + b[dim].lower); + ymid = Sample(t, xmid); + + maxdev = 0; + maxdim = 0; + for( dim = 0; dim < t->ndim; ++dim ) { + real ylower, yupper, dev; + creal x = xmid[dim]; + xmid[dim] = b[dim].lower; + ylower = Sample(t, xmid); + xmid[dim] = b[dim].upper; + yupper = Sample(t, xmid); + xmid[dim] = x; + + dev = fabs(ymid - .5*(ylower + yupper)); + if( dev >= maxdev ) { + maxdev = dev; + maxdim = dim; + } + } + + if( maxdev > 0 ) nextdim = 0; + else maxdim = nextdim++ % t->ndim; + + cut->i = Upper(maxdim); + cut->save = b[maxdim].upper; + b[maxdim].upper = xmid[maxdim]; +} + +/*********************************************************************/ + +static inline real Volume(cThis *t, creal *delta) +{ + real vol = 1; + count dim; + for( dim = 0; dim < t->ndim; ++dim ) + vol *= delta[Lower(dim)] + delta[Upper(dim)]; + return vol; +} + +/*********************************************************************/ + +static inline real SetupEqs(Cut *cut, ccount ncuts, real f) +{ + real sqsum = 0; + Cut *c = &cut[ncuts]; + while( --c >= cut ) { + sqsum += Sq(c->lhs = f - c->f); + f = c->f; + } + return sqsum; +} + +/*********************************************************************/ + +static inline void SolveEqs(Cut *cut, count ncuts, + creal *delta, creal diff) +{ + real last = 0; + real r = 1; + Cut *c; + + for( c = cut; ; ++c ) { + ccount dim = Dim(c->i); + c->row = r -= + Div(diff, (delta[Lower(dim)] + delta[Upper(dim)])*c->df); + if( --ncuts == 0 ) break; + last += r*c->lhs; + } + + last = Div(c->lhs - last, r); + + for( ; c >= cut; last += (--c)->lhs ) { + creal delmin = -(c->delta = delta[c->i]); + creal delmax = FRACT*(delmin + c->save); + c->sol = Div(last, c->df); + if( c->sol > delmax ) c->sol = .75*delmax; + if( c->sol < delmin ) c->sol = .75*delmin; + } +} + +/*********************************************************************/ + +static count FindCuts(This *t, Cut *cut, Bounds *bounds, creal vol, + real *xmajor, creal fmajor, creal fdiff) +{ + cint sign = (fdiff < 0) ? -1 : 1; + + count ncuts = 0, icut; + real delta[2*NDIM]; + real gamma, fgamma, lhssq; + count dim, div; + + for( dim = 0; dim < t->ndim; ++dim ) { + cBounds *b = &bounds[dim]; + creal xsave = xmajor[dim]; + real dist = b->upper - xsave; + if( dist >= BNDTOL*(b->upper - b->lower) ) { + Cut *c = &cut[ncuts++]; + c->i = Upper(dim); + c->save = dist; + xmajor[dim] += dist *= FRACT; + c->f = Sample(t, xmajor); + xmajor[dim] = xsave; + } + delta[Upper(dim)] = dist; + } + + for( dim = 0; dim < t->ndim; ++dim ) { + cBounds *b = &bounds[dim]; + creal xsave = xmajor[dim]; + real dist = xsave - b->lower; + if( dist >= BNDTOL*(b->upper - b->lower) ) { + Cut *c = &cut[ncuts++]; + c->i = Lower(dim); + c->save = dist; + xmajor[dim] -= dist *= FRACT; + c->f = Sample(t, xmajor); + xmajor[dim] = xsave; + } + delta[Lower(dim)] = dist; + } + + if( ncuts == 0 ) { + SomeCut(t, cut, bounds); + return 1; + } + + for( ; ; ) { + real mindiff = INFTY; + Cut *mincut = cut; + + for( icut = 0; icut < ncuts; ++icut ) { + Cut *c = &cut[icut]; + creal diff = fabs(fmajor - c->f); + if( diff <= mindiff ) { + mindiff = diff; + mincut = c; + } + } + + gamma = Volume(t, delta)/vol; + fgamma = fmajor + (gamma - 1)*fdiff; + + if( sign*(mincut->f - fgamma) < 0 ) break; + + if( --ncuts == 0 ) { + SomeCut(t, cut, bounds); + return 1; + } + + delta[mincut->i] = mincut->save; + memcpy(mincut, mincut + 1, (char *)&cut[ncuts] - (char *)mincut); + } + + for( icut = 0; icut < ncuts; ++icut ) { + Cut *c = &cut[icut]; + c->fold = c->f; + c->df = (c->f - fmajor)/delta[c->i]; + } + + lhssq = SetupEqs(cut, ncuts, fgamma); + +repeat: + SolveEqs(cut, ncuts, delta, gamma*fdiff); + + for( div = 1; div <= 16; div *= 4 ) { + real gammanew, lhssqnew; + + for( icut = 0; icut < ncuts; ++icut ) { + Cut *c = &cut[icut]; + real *x = &xmajor[Dim(c->i)]; + creal xsave = *x; + delta[c->i] = c->delta + c->sol/div; + *x += SignedDelta(c->i); + c->f = Sample(t, xmajor); + *x = xsave; + } + + gammanew = Volume(t, delta)/vol; + fgamma = fmajor + (gammanew - 1)*fdiff; + lhssqnew = SetupEqs(cut, ncuts, fgamma); + + if( lhssqnew <= lhssq ) { + real fmax; + + if( fabs(gammanew - gamma) < GAMMATOL*gamma ) break; + gamma = gammanew; + + fmax = fabs(fgamma); + for( icut = 0; icut < ncuts; ++icut ) { + Cut *c = &cut[icut]; + creal dfmin = SINGTOL*c->df; + creal sol = c->sol/div; + real df = c->f - c->fold; + df = (fabs(sol) < BIG*fabs(df)) ? df/sol : 1; + c->df = (fabs(df) < fabs(dfmin)) ? dfmin : df; + fmax = Max(fmax, fabs(c->f)); + c->fold = c->f; + } + + if( lhssqnew < Sq((1 + fmax)*LHSTOL) ) break; + lhssq = lhssqnew; + goto repeat; + } + } + + for( icut = 0; icut < ncuts; ++icut ) { + Cut *c = &cut[icut]; + real *b = (real *)bounds + c->i; + c->save = *b; + *b = xmajor[Dim(c->i)] + SignedDelta(c->i); + } + + return ncuts; +} + +/*********************************************************************/ + +static void Split(This *t, ccount iregion) +{ + TYPEDEFREGION; + Region *region = RegionPtr(iregion); + Cut cut[2*NDIM], *c; + Errors errors[NCOMP]; + count ncuts, succ; + int depth; + real *b, tmp; + + t->selectedcomp = region->cutcomp; + t->neval_cut -= t->neval; + ncuts = FindCuts(t, cut, region->bounds, region->vol, + (real *)region->result + region->xmajor, region->fmajor, + region->fmajor - region->fminor); + t->neval_cut += t->neval; + + depth = region->depth - ncuts; + + EnlargeRegions(t, ++ncuts); + region = RegionPtr(iregion); + region->depth = -ncuts; + succ = iregion + region->next; + region->next = t->nregions - iregion; + b = (real *)region->bounds; + + region = RegionPtr(t->nregions); + VecCopy(region->bounds, b); + region->depth = IDim(depth) + 1; + region->next = 1; + region->isamples = 0; + for( c = cut; --ncuts; ++c ) { + ccount ci = c->i; + creal tmp = b[ci ^ 1]; + b[ci ^ 1] = b[ci]; + b[ci] = c->save; + region = RegionPtr(++t->nregions); + VecCopy(region->bounds, b); + region->depth = IDim(depth) + 1; + region->next = 1; + region->isamples = 0; + ++depth; + b[ci ^ 1] = tmp; + } + region->next = succ - t->nregions++; +} + |