1 /*
2   Teem: Tools to process and visualize scientific data and images             .
3   Copyright (C) 2012, 2011, 2010, 2009  University of Chicago
4   Copyright (C) 2008, 2007, 2006, 2005  Gordon Kindlmann
5   Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998  University of Utah
6 
7   This library is free software; you can redistribute it and/or
8   modify it under the terms of the GNU Lesser General Public License
9   (LGPL) as published by the Free Software Foundation; either
10   version 2.1 of the License, or (at your option) any later version.
11   The terms of redistributing and/or modifying this software also
12   include exceptions to the LGPL that facilitate static linking.
13 
14   This library is distributed in the hope that it will be useful,
15   but WITHOUT ANY WARRANTY; without even the implied warranty of
16   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17   Lesser General Public License for more details.
18 
19   You should have received a copy of the GNU Lesser General Public License
20   along with this library; if not, write to Free Software Foundation, Inc.,
21   51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
22 */
23 
24 #include "ten.h"
25 #include "privateTen.h"
26 
27 tenGradientParm *
tenGradientParmNew(void)28 tenGradientParmNew(void) {
29   tenGradientParm *ret;
30 
31   ret = (tenGradientParm *)calloc(1, sizeof(tenGradientParm));
32   if (ret) {
33     ret->initStep = 1.0;
34     ret->jitter = 0.2;
35     ret->minVelocity = 0.000000001;
36     ret->minPotentialChange = 0.000000001;
37     ret->minMean = 0.0001;
38     ret->minMeanImprovement = 0.00005;
39     ret->single = AIR_FALSE;
40     ret->insertZeroVec = AIR_FALSE;
41     ret->verbose = 1;
42     ret->snap = 0;
43     ret->report = 400;
44     ret->expo = 1;
45     ret->expo_d = 0;
46     ret->seed = 42;
47     ret->maxEdgeShrink = 20;
48     ret->minIteration = 0;
49     ret->maxIteration = 1000000;
50     ret->step = 0;
51     ret->nudge = 0;
52     ret->itersUsed = 0;
53     ret->potential = 0;
54     ret->potentialNorm = 0;
55     ret->angle = 0;
56     ret->edge = 0;
57   }
58   return ret;
59 }
60 
61 tenGradientParm *
tenGradientParmNix(tenGradientParm * tgparm)62 tenGradientParmNix(tenGradientParm *tgparm) {
63 
64   airFree(tgparm);
65   return NULL;
66 }
67 
68 int
tenGradientCheck(const Nrrd * ngrad,int type,unsigned int minnum)69 tenGradientCheck(const Nrrd *ngrad, int type, unsigned int minnum) {
70   static const char me[]="tenGradientCheck";
71 
72   if (nrrdCheck(ngrad)) {
73     biffMovef(TEN, NRRD, "%s: basic validity check failed", me);
74     return 1;
75   }
76   if (!( 3 == ngrad->axis[0].size && 2 == ngrad->dim )) {
77     char stmp[AIR_STRLEN_SMALL];
78     biffAddf(TEN, "%s: need a 3xN 2-D array (not a %sx? %u-D array)", me,
79              airSprintSize_t(stmp, ngrad->axis[0].size), ngrad->dim);
80     return 1;
81   }
82   if (nrrdTypeDefault != type && type != ngrad->type) {
83     biffAddf(TEN, "%s: requested type %s but got type %s", me,
84              airEnumStr(nrrdType, type), airEnumStr(nrrdType, ngrad->type));
85     return 1;
86   }
87   if (nrrdTypeBlock == ngrad->type) {
88     biffAddf(TEN, "%s: sorry, can't use %s type", me,
89              airEnumStr(nrrdType, nrrdTypeBlock));
90     return 1;
91   }
92   if (!( minnum <= ngrad->axis[1].size )) {
93     char stmp[AIR_STRLEN_SMALL];
94     biffAddf(TEN, "%s: have only %s gradients, need at least %d", me,
95              airSprintSize_t(stmp, ngrad->axis[1].size), minnum);
96     return 1;
97   }
98 
99   return 0;
100 }
101 
102 /*
103 ******** tenGradientRandom
104 **
105 ** generates num random unit vectors of type double
106 */
107 int
tenGradientRandom(Nrrd * ngrad,unsigned int num,unsigned int seed)108 tenGradientRandom(Nrrd *ngrad, unsigned int num, unsigned int seed) {
109   static const char me[]="tenGradientRandom";
110   double *grad, len;
111   unsigned int gi;
112 
113   if (nrrdMaybeAlloc_va(ngrad, nrrdTypeDouble, 2,
114                         AIR_CAST(size_t, 3), AIR_CAST(size_t, num))) {
115     biffMovef(TEN, NRRD, "%s: couldn't allocate output", me);
116     return 1;
117   }
118   airSrandMT(seed);
119   grad = AIR_CAST(double*, ngrad->data);
120   for (gi=0; gi<num; gi++) {
121     do {
122       grad[0] = AIR_AFFINE(0, airDrandMT(), 1, -1, 1);
123       grad[1] = AIR_AFFINE(0, airDrandMT(), 1, -1, 1);
124       grad[2] = AIR_AFFINE(0, airDrandMT(), 1, -1, 1);
125       len = ELL_3V_LEN(grad);
126     } while (len > 1 || !len);
127     ELL_3V_SCALE(grad, 1.0/len, grad);
128     grad += 3;
129   }
130   return 0;
131 }
132 
133 /*
134 ******** tenGradientIdealEdge
135 **
136 ** edge length of delauney triangulation of idealized distribution of
137 ** N gradients (2*N points), but also allowing a boolean "single" flag
138 ** saying that we actually care about N points
139 */
140 double
tenGradientIdealEdge(unsigned int N,int single)141 tenGradientIdealEdge(unsigned int N, int single) {
142 
143   return sqrt((!single ? 4 : 8)*AIR_PI/(sqrt(3)*N));
144 }
145 
146 /*
147 ******** tenGradientJitter
148 **
149 ** moves all gradients by amount dist on tangent plane, in a random
150 ** direction, and then renormalizes. The distance is a fraction
151 ** of the ideal edge length (via tenGradientIdealEdge)
152 */
153 int
tenGradientJitter(Nrrd * nout,const Nrrd * nin,double dist)154 tenGradientJitter(Nrrd *nout, const Nrrd *nin, double dist) {
155   static const char me[]="tenGradientJitter";
156   double *grad, perp0[3], perp1[3], len, theta, cc, ss, edge;
157   unsigned int gi, num;
158 
159   if (nrrdConvert(nout, nin, nrrdTypeDouble)) {
160     biffMovef(TEN, NRRD, "%s: trouble converting input to double", me);
161     return 1;
162   }
163   if (tenGradientCheck(nout, nrrdTypeDouble, 3)) {
164     biffAddf(TEN, "%s: didn't get valid gradients", me);
165     return 1;
166   }
167   grad = AIR_CAST(double*, nout->data);
168   num = AIR_UINT(nout->axis[1].size);
169   /* HEY: possible confusion between single and not */
170   edge = tenGradientIdealEdge(num, AIR_FALSE);
171   for (gi=0; gi<num; gi++) {
172     ELL_3V_NORM(grad, grad, len);
173     ell_3v_perp_d(perp0, grad);
174     ELL_3V_CROSS(perp1, perp0, grad);
175     theta = AIR_AFFINE(0, airDrandMT(), 1, 0, 2*AIR_PI);
176     cc = dist*edge*cos(theta);
177     ss = dist*edge*sin(theta);
178     ELL_3V_SCALE_ADD3(grad, 1.0, grad, cc, perp0, ss, perp1);
179     ELL_3V_NORM(grad, grad, len);
180     grad += 3;
181   }
182 
183   return 0;
184 }
185 
186 void
tenGradientMeasure(double * pot,double * minAngle,double * minEdge,const Nrrd * npos,tenGradientParm * tgparm,int edgeNormalize)187 tenGradientMeasure(double *pot, double *minAngle, double *minEdge,
188                    const Nrrd *npos, tenGradientParm *tgparm,
189                    int edgeNormalize) {
190   /* static const char me[]="tenGradientMeasure"; */
191   double diff[3], *pos, atmp=0, ptmp, edge, len;
192   unsigned int ii, jj, num;
193 
194   /* allow minAngle NULL */
195   if (!(pot && npos && tgparm )) {
196     return;
197   }
198 
199   num = AIR_UINT(npos->axis[1].size);
200   pos = AIR_CAST(double *, npos->data);
201   edge = (edgeNormalize
202           ? tenGradientIdealEdge(num, tgparm->single)
203           : 1.0);
204   *pot = 0;
205   if (minAngle) {
206     *minAngle = AIR_PI;
207   }
208   if (minEdge) {
209     *minEdge = 2;
210   }
211   for (ii=0; ii<num; ii++) {
212     for (jj=0; jj<ii; jj++) {
213       ELL_3V_SUB(diff, pos + 3*ii, pos + 3*jj);
214       len = ELL_3V_LEN(diff);
215       if (minEdge) {
216         *minEdge = AIR_MIN(*minEdge, len);
217       }
218       if (tgparm->expo) {
219         ptmp = airIntPow(edge/len, tgparm->expo);
220       } else {
221         ptmp = pow(edge/len, tgparm->expo_d);
222       }
223       *pot += ptmp;
224       if (minAngle) {
225         atmp = ell_3v_angle_d(pos + 3*ii, pos + 3*jj);
226         *minAngle = AIR_MIN(atmp, *minAngle);
227       }
228       if (!tgparm->single) {
229         *pot += ptmp;
230         ELL_3V_ADD2(diff, pos + 3*ii, pos + 3*jj);
231         len = ELL_3V_LEN(diff);
232         if (minEdge) {
233           *minEdge = AIR_MIN(*minEdge, len);
234         }
235         if (tgparm->expo) {
236           *pot += 2*airIntPow(edge/len, tgparm->expo);
237         } else {
238           *pot += 2*pow(edge/len, tgparm->expo_d);
239         }
240         if (minAngle) {
241           *minAngle = AIR_MIN(AIR_PI-atmp, *minAngle);
242         }
243       }
244     }
245   }
246   return;
247 }
248 
249 /*
250 ** Do asynchronous update of positions in "npos', based on force
251 ** calculations wherein the distances are normalized "edge".  Using a
252 ** small "edge" allows forces to either underflow to zero, or be
253 ** finite, instead of exploding to infinity, for high exponents.
254 **
255 ** The smallest seen edge length is recorded in "*edgeMin", which is
256 ** initialized to the given "edge".  This allows, for example, the
257 ** caller to try again with a smaller edge normalization.
258 **
259 ** The mean velocity of the points through the update is recorded in
260 ** "*meanVel".
261 **
262 ** Based on the observation that when using large exponents, numerical
263 ** difficulties arise from the (force-based) update of the positions
264 ** of the two (or few) closest particles, this function puts a speed
265 ** limit (variable "limit") on the distance a particle may move during
266 ** update, expressed as a fraction of the normalizing edge length.
267 ** "limit" has been set heuristically, according to the exponent (we
268 ** have to clamp speeds more aggresively with higher exponents), as
269 ** well as (even more heuristically) according to the number of times
270 ** the step size has been decreased.  This latter factor has to be
271 ** bounded, so that the update is not unnecessarily bounded when the
272 ** step size gets very small at the last stages of computation.
273 ** Without the step-size-based speed limit, the step size would
274 ** sometimes (e.g. num=200, expo=300) have to reduced to a miniscule
275 ** value, which slows subsequent convergence terribly.
276 **
277 ** this function is not static, though it could be, so that mac's
278 ** "Sampler" app can profile this
279 */
280 int
_tenGradientUpdate(double * meanVel,double * edgeMin,Nrrd * npos,double edge,tenGradientParm * tgparm)281 _tenGradientUpdate(double *meanVel, double *edgeMin,
282                    Nrrd *npos, double edge, tenGradientParm *tgparm) {
283   /* static const char me[]="_tenGradientUpdate"; */
284   double *pos, newpos[3], grad[3], ngrad[3],
285     dir[3], len, rep, step, diff[3], limit, expo;
286   int num, ii, jj, E;
287 
288   E = 0;
289   pos = AIR_CAST(double *, npos->data);
290   num = AIR_UINT(npos->axis[1].size);
291   *meanVel = 0;
292   *edgeMin = edge;
293   expo = tgparm->expo ? tgparm->expo : tgparm->expo_d;
294   limit = expo*AIR_MIN(sqrt(expo),
295                        log(1 + tgparm->initStep/tgparm->step));
296   for (ii=0; ii<num; ii++) {
297     ELL_3V_SET(grad, 0, 0, 0);
298     for (jj=0; jj<num; jj++) {
299       if (ii == jj) {
300         continue;
301       }
302       ELL_3V_SUB(dir, pos + 3*ii, pos + 3*jj);
303       ELL_3V_NORM(dir, dir, len);
304       *edgeMin = AIR_MIN(*edgeMin, len);
305       if (tgparm->expo) {
306         rep = airIntPow(edge/len, tgparm->expo+1);
307       } else {
308         rep = pow(edge/len, tgparm->expo_d+1);
309       }
310       ELL_3V_SCALE_INCR(grad, rep/num, dir);
311       if (!tgparm->single) {
312         ELL_3V_ADD2(dir, pos + 3*ii, pos + 3*jj);
313         ELL_3V_NORM(dir, dir, len);
314         *edgeMin = AIR_MIN(*edgeMin, len);
315         if (tgparm->expo) {
316           rep = airIntPow(edge/len, tgparm->expo+1);
317         } else {
318           rep = pow(edge/len, tgparm->expo_d+1);
319         }
320         ELL_3V_SCALE_INCR(grad, rep/num, dir);
321       }
322     }
323     ELL_3V_NORM(ngrad, grad, len);
324     if (!( AIR_EXISTS(len) )) {
325       /* things blew up, either in incremental force
326          additions, or in the attempt at normalization */
327       E = 1;
328       *meanVel = AIR_NAN;
329       break;
330     }
331     if (0 == len) {
332       /* if the length of grad[] underflowed to zero, we can
333          legitimately zero out ngrad[] */
334       ELL_3V_SET(ngrad, 0, 0, 0);
335     }
336     step = AIR_MIN(len*tgparm->step, edge/limit);
337     ELL_3V_SCALE_ADD2(newpos,
338                       1.0, pos + 3*ii,
339                       step, ngrad);
340     ELL_3V_NORM(newpos, newpos, len);
341     ELL_3V_SUB(diff, pos + 3*ii, newpos);
342     *meanVel += ELL_3V_LEN(diff);
343     ELL_3V_COPY(pos + 3*ii, newpos);
344   }
345   *meanVel /= num;
346 
347   return E;
348 }
349 
350 /*
351 ** assign random signs to the vectors and measures the length of their
352 ** mean, as quickly as possible
353 */
354 static double
party(Nrrd * npos,airRandMTState * rstate)355 party(Nrrd *npos, airRandMTState *rstate) {
356   double *pos, mean[3];
357   unsigned int ii, num, rnd, rndBit;
358 
359   pos = (double *)(npos->data);
360   num = AIR_UINT(npos->axis[1].size);
361   rnd = airUIrandMT_r(rstate);
362   rndBit = 0;
363   ELL_3V_SET(mean, 0, 0, 0);
364   for (ii=0; ii<num; ii++) {
365     if (32 == rndBit) {
366       rnd = airUIrandMT_r(rstate);
367       rndBit = 0;
368     }
369     if (rnd & (1 << rndBit++)) {
370       ELL_3V_SCALE(pos + 3*ii, -1, pos + 3*ii);
371     }
372     ELL_3V_INCR(mean, pos + 3*ii);
373   }
374   ELL_3V_SCALE(mean, 1.0/num, mean);
375   return ELL_3V_LEN(mean);
376 }
377 
378 /*
379 ** parties until the gradients settle down
380 */
381 int
tenGradientBalance(Nrrd * nout,const Nrrd * nin,tenGradientParm * tgparm)382 tenGradientBalance(Nrrd *nout, const Nrrd *nin,
383                    tenGradientParm *tgparm) {
384   static const char me[]="tenGradientBalance";
385   double len, lastLen, improv;
386   airRandMTState *rstate;
387   Nrrd *ncopy;
388   unsigned int iter, maxIter;
389   int done;
390   airArray *mop;
391 
392   if (!nout || tenGradientCheck(nin, nrrdTypeUnknown, 2) || !tgparm) {
393     biffAddf(TEN, "%s: got NULL pointer (%p,%p) or invalid nin", me,
394              AIR_VOIDP(nout), AIR_VOIDP(tgparm));
395     return 1;
396   }
397   if (nrrdConvert(nout, nin, nrrdTypeDouble)) {
398     biffMovef(TEN, NRRD, "%s: can't initialize output with input", me);
399     return 1;
400   }
401 
402   mop = airMopNew();
403   ncopy = nrrdNew();
404   airMopAdd(mop, ncopy, (airMopper)nrrdNuke, airMopAlways);
405   rstate = airRandMTStateNew(tgparm->seed);
406   airMopAdd(mop, rstate, (airMopper)airRandMTStateNix, airMopAlways);
407   /* HEY: factor of 100 is an approximate hack */
408   maxIter = 100*tgparm->maxIteration;
409 
410   lastLen = 1.0;
411   done = AIR_FALSE;
412   do {
413     iter = 0;
414     do {
415       iter++;
416       len = party(nout, rstate);
417     } while (len > lastLen && iter < maxIter);
418     if (iter >= maxIter) {
419       if (tgparm->verbose) {
420         fprintf(stderr, "%s: stopping at max iter %u\n", me, maxIter);
421       }
422       if (nrrdCopy(nout, ncopy)) {
423         biffMovef(TEN, NRRD, "%s: trouble copying", me);
424         airMopError(mop); return 1;
425       }
426       done = AIR_TRUE;
427     } else {
428       if (nrrdCopy(ncopy, nout)) {
429         biffMovef(TEN, NRRD, "%s: trouble copying", me);
430         airMopError(mop); return 1;
431       }
432       improv = lastLen - len;
433       lastLen = len;
434       if (tgparm->verbose) {
435         fprintf(stderr, "%s: (iter %u) improvement: %g  (mean length = %g)\n",
436                 me, iter, improv, len);
437       }
438       done = (improv <= tgparm->minMeanImprovement
439               || len < tgparm->minMean);
440     }
441   } while (!done);
442 
443   airMopOkay(mop);
444   return 0;
445 }
446 
447 /*
448 ******** tenGradientDistribute
449 **
450 ** Takes the given list of gradients, normalizes their lengths,
451 ** optionally jitters their positions, does point repulsion, and then
452 ** (optionally) selects a combination of directions with minimum vector sum.
453 **
454 ** The complicated part of this is the point repulsion, which uses a
455 ** gradient descent with variable set size. The progress of the system
456 ** is measured by decrease in potential (when its measurement doesn't
457 ** overflow to infinity) or an increase in the minimum angle.  When a
458 ** step results in negative progress, the step size is halved, and the
459 ** iteration is attempted again.  Based on the observation that at
460 ** some points the step size must be made very small to get progress,
461 ** the step size is cautiously increased ("nudged") at every
462 ** iteration, to try to avoid using an overly small step.  The amount
463 ** by which the step is nudged is halved everytime the step is halved,
464 ** to avoid endless cycling through step sizes.
465 */
466 int
tenGradientDistribute(Nrrd * nout,const Nrrd * nin,tenGradientParm * tgparm)467 tenGradientDistribute(Nrrd *nout, const Nrrd *nin,
468                       tenGradientParm *tgparm) {
469   static const char me[]="tenGradientDistribute";
470   char filename[AIR_STRLEN_SMALL];
471   unsigned int ii, num, iter, oldIdx, newIdx, edgeShrink;
472   airArray *mop;
473   Nrrd *npos[2];
474   double *pos, len, meanVelocity, pot, potNew, potD,
475     edge, edgeMin, angle, angleNew;
476   int E;
477 
478   if (!nout || tenGradientCheck(nin, nrrdTypeUnknown, 2) || !tgparm) {
479     biffAddf(TEN, "%s: got NULL pointer or invalid input", me);
480     return 1;
481   }
482 
483   num = AIR_UINT(nin->axis[1].size);
484   mop = airMopNew();
485   npos[0] = nrrdNew();
486   npos[1] = nrrdNew();
487   airMopAdd(mop, npos[0], (airMopper)nrrdNuke, airMopAlways);
488   airMopAdd(mop, npos[1], (airMopper)nrrdNuke, airMopAlways);
489   if (nrrdConvert(npos[0], nin, nrrdTypeDouble)
490       || nrrdConvert(npos[1], nin, nrrdTypeDouble)) {
491     biffMovef(TEN, NRRD, "%s: trouble allocating temp buffers", me);
492     airMopError(mop); return 1;
493   }
494 
495   pos = (double*)(npos[0]->data);
496   for (ii=0; ii<num; ii++) {
497     ELL_3V_NORM(pos, pos, len);
498     pos += 3;
499   }
500   if (tgparm->jitter) {
501     if (tenGradientJitter(npos[0], npos[0], tgparm->jitter)) {
502       biffAddf(TEN, "%s: problem jittering input", me);
503       airMopError(mop); return 1;
504     }
505   }
506 
507   /* initialize things prior to first iteration; have to
508      make sure that loop body tests pass 1st time around */
509   meanVelocity = 2*tgparm->minVelocity;
510   potD = -2*tgparm->minPotentialChange;
511   oldIdx = 0;
512   newIdx = 1;
513   tgparm->step = tgparm->initStep;
514   tgparm->nudge = 0.1;
515   tenGradientMeasure(&pot, &angle, NULL,
516                      npos[oldIdx], tgparm, AIR_TRUE);
517   for (iter = 0;
518        ((!!tgparm->minIteration && iter < tgparm->minIteration)
519         ||
520         (iter < tgparm->maxIteration
521          && (!tgparm->minPotentialChange
522              || !AIR_EXISTS(potD)
523              || -potD > tgparm->minPotentialChange)
524          && (!tgparm->minVelocity
525              || meanVelocity > tgparm->minVelocity)
526          && tgparm->step > FLT_MIN));
527        iter++) {
528     /* copy positions from old to new */
529     memcpy(npos[newIdx]->data, npos[oldIdx]->data, 3*num*sizeof(double));
530     edge = tenGradientIdealEdge(num, tgparm->single);
531     edgeShrink = 0;
532     /* try to do a position update, which will fail if repulsion values
533        explode, from having an insufficiently small edge normalization,
534        so retry with smaller edge next time */
535     do {
536       E = _tenGradientUpdate(&meanVelocity, &edgeMin,
537                              npos[newIdx], edge, tgparm);
538       if (E) {
539         if (edgeShrink > tgparm->maxEdgeShrink) {
540           biffAddf(TEN, "%s: %u > %u edge shrinks (%g), update still failed",
541                   me, edgeShrink, tgparm->maxEdgeShrink, edge);
542           airMopError(mop); return 1;
543         }
544         edgeShrink++;
545         /* re-initialize positions (HEY ugly code logic) */
546         memcpy(npos[newIdx]->data, npos[oldIdx]->data, 3*num*sizeof(double));
547         edge = edgeMin;
548       }
549     } while (E);
550     tenGradientMeasure(&potNew, &angleNew, NULL,
551                        npos[newIdx], tgparm, AIR_TRUE);
552     if ((AIR_EXISTS(pot) && AIR_EXISTS(potNew) && potNew <= pot)
553         || angleNew >= angle) {
554       /* there was progress of some kind, either through potential
555          decrease, or angle increase */
556       potD = 2*(potNew - pot)/(potNew + pot);
557       if (!(iter % tgparm->report) && tgparm->verbose) {
558         fprintf(stderr, "%s(%d): . . . . . . step = %g, edgeShrink = %u\n"
559                 "   velo = %g<>%g, phi = %g ~ %g<>%g, angle = %g ~ %g\n",
560                 me, iter, tgparm->step, edgeShrink,
561                 meanVelocity, tgparm->minVelocity,
562                 pot, potD, tgparm->minPotentialChange,
563                 angle, angleNew - angle);
564       }
565       if (tgparm->snap && !(iter % tgparm->snap)) {
566         sprintf(filename, "%05d.nrrd", iter/tgparm->snap);
567         if (tgparm->verbose) {
568           fprintf(stderr, "%s(%d): . . . . . . saving %s\n",
569                   me, iter, filename);
570         }
571         if (nrrdSave(filename, npos[newIdx], NULL)) {
572           char *serr;
573           serr = biffGetDone(NRRD);
574           if (tgparm->verbose) { /* perhaps shouldn't have this check */
575             fprintf(stderr, "%s: iter=%d, couldn't save snapshot:\n%s"
576                     "continuing ...\n", me, iter, serr);
577           }
578           free(serr);
579         }
580       }
581       tgparm->step *= 1 + tgparm->nudge;
582       tgparm->step = AIR_MIN(tgparm->initStep, tgparm->step);
583       pot = potNew;
584       angle = angleNew;
585       /* swap buffers */
586       newIdx = 1 - newIdx;
587       oldIdx = 1 - oldIdx;
588     } else {
589       /* oops, did not make progress; back off and try again */
590       if (tgparm->verbose) {
591         fprintf(stderr, "%s(%d): ######## step %g --> %g\n"
592                 " phi = %g --> %g ~ %g, angle = %g --> %g\n",
593                 me, iter, tgparm->step, tgparm->step/2,
594                 pot, potNew, potD, angle, angleNew);
595       }
596       tgparm->step /= 2;
597       tgparm->nudge /= 2;
598     }
599   }
600 
601   /* when the for-loop test fails, we stop before computing the next
602      iteration (which starts with copying from npos[oldIdx] to
603      npos[newIdx]) ==> the final results are in npos[oldIdx] */
604 
605   if (tgparm->verbose) {
606     fprintf(stderr, "%s: .......................... done distribution:\n"
607             "  (%d && %d) || (%d \n"
608             "               && (%d || %d || %d) \n"
609             "               && (%d || %d) \n"
610             "               && %d) is false\n", me,
611             !!tgparm->minIteration, iter < tgparm->minIteration,
612             iter < tgparm->maxIteration,
613             !tgparm->minPotentialChange,
614             !AIR_EXISTS(potD), AIR_ABS(potD) > tgparm->minPotentialChange,
615             !tgparm->minVelocity, meanVelocity > tgparm->minVelocity,
616             tgparm->step > FLT_MIN);
617     fprintf(stderr, "  iter=%d, velo = %g<>%g, phi = %g ~ %g<>%g;\n",
618             iter, meanVelocity, tgparm->minVelocity, pot,
619             potD, tgparm->minPotentialChange);
620     fprintf(stderr, "  minEdge = %g; idealEdge = %g\n",
621             2*sin(angle/2), tenGradientIdealEdge(num, tgparm->single));
622   }
623 
624   tenGradientMeasure(&pot, NULL, NULL, npos[oldIdx], tgparm, AIR_FALSE);
625   tgparm->potential = pot;
626   tenGradientMeasure(&pot, &angle, &edge, npos[oldIdx], tgparm, AIR_TRUE);
627   tgparm->potentialNorm = pot;
628   tgparm->angle = angle;
629   tgparm->edge = edge;
630   tgparm->itersUsed = iter;
631 
632   if ((tgparm->minMeanImprovement || tgparm->minMean)
633       && !tgparm->single) {
634     if (tgparm->verbose) {
635       fprintf(stderr, "%s: optimizing balance:\n", me);
636     }
637     if (tenGradientBalance(nout, npos[oldIdx], tgparm)) {
638       biffAddf(TEN, "%s: failed to minimize vector sum of gradients", me);
639       airMopError(mop); return 1;
640     }
641     if (tgparm->verbose) {
642       fprintf(stderr, "%s: .......................... done balancing.\n", me);
643     }
644   } else {
645     if (tgparm->verbose) {
646       fprintf(stderr, "%s: .......................... (no balancing)\n", me);
647     }
648     if (nrrdConvert(nout, npos[oldIdx], nrrdTypeDouble)) {
649       biffMovef(TEN, NRRD, "%s: couldn't set output", me);
650       airMopError(mop); return 1;
651     }
652   }
653 
654   airMopOkay(mop);
655   return 0;
656 }
657 
658 /*
659 ** note that if tgparm->insertZeroVec, there will be one sample more
660 ** along axis 1 of nout than the requested #gradients "num"
661 */
662 int
tenGradientGenerate(Nrrd * nout,unsigned int num,tenGradientParm * tgparm)663 tenGradientGenerate(Nrrd *nout, unsigned int num, tenGradientParm *tgparm) {
664   static const char me[]="tenGradientGenerate";
665   Nrrd *nin;
666   airArray *mop;
667 
668   if (!(nout && tgparm)) {
669     biffAddf(TEN, "%s: got NULL pointer", me);
670     return 1;
671   }
672   if (!( num >= 3 )) {
673     biffAddf(TEN, "%s: can generate minimum of 3 gradient directions "
674             "(not %d)", me, num);
675     return 1;
676   }
677   mop = airMopNew();
678   nin = nrrdNew();
679   airMopAdd(mop, nin, (airMopper)nrrdNuke, airMopAlways);
680 
681   if (tenGradientRandom(nin, num, tgparm->seed)
682       || tenGradientDistribute(nout, nin, tgparm)) {
683     biffAddf(TEN, "%s: trouble", me);
684     airMopError(mop); return 1;
685   }
686   if (tgparm->insertZeroVec) {
687     /* this is potentially confusing: the second axis (axis 1)
688        is going to come back one longer than the requested
689        number of gradients! */
690     Nrrd *ntmp;
691     ptrdiff_t padMin[2] = {0, -1}, padMax[2];
692     padMax[0] = AIR_CAST(ptrdiff_t, nout->axis[0].size-1);
693     padMax[1] = AIR_CAST(ptrdiff_t, num-1);
694     ntmp = nrrdNew();
695     airMopAdd(mop, ntmp, (airMopper)nrrdNuke, airMopAlways);
696     if (nrrdPad_nva(ntmp, nout, padMin, padMax,
697                     nrrdBoundaryPad, 0.0)
698         || nrrdCopy(nout, ntmp)) {
699       biffMovef(TEN, NRRD, "%s: trouble adding zero vector", me);
700       airMopError(mop); return 1;
701     }
702   }
703 
704   airMopOkay(mop);
705   return 0;
706 }
707