GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: src/ten/grads.c Lines: 272 376 72.3 %
Date: 2017-05-26 Branches: 131 226 58.0 %

Line Branch Exec Source
1
/*
2
  Teem: Tools to process and visualize scientific data and images             .
3
  Copyright (C) 2013, 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 *
28
tenGradientParmNew(void) {
29
  tenGradientParm *ret;
30
31
18
  ret = (tenGradientParm *)calloc(1, sizeof(tenGradientParm));
32
9
  if (ret) {
33
9
    ret->initStep = 1.0;
34
9
    ret->jitter = 0.2;
35
9
    ret->minVelocity = 0.000000001;
36
9
    ret->minPotentialChange = 0.000000001;
37
9
    ret->minMean = 0.0001;
38
9
    ret->minMeanImprovement = 0.00005;
39
9
    ret->single = AIR_FALSE;
40
9
    ret->insertZeroVec = AIR_FALSE;
41
9
    ret->verbose = 1;
42
9
    ret->snap = 0;
43
9
    ret->report = 400;
44
9
    ret->expo = 1;
45
9
    ret->expo_d = 0;
46
9
    ret->seed = 42;
47
9
    ret->maxEdgeShrink = 20;
48
9
    ret->minIteration = 0;
49
9
    ret->maxIteration = 1000000;
50
9
    ret->step = 0;
51
9
    ret->nudge = 0;
52
9
    ret->itersUsed = 0;
53
9
    ret->potential = 0;
54
9
    ret->potentialNorm = 0;
55
9
    ret->angle = 0;
56
9
    ret->edge = 0;
57
9
  }
58
9
  return ret;
59
}
60
61
tenGradientParm *
62
tenGradientParmNix(tenGradientParm *tgparm) {
63
64
18
  airFree(tgparm);
65
9
  return NULL;
66
}
67
68
int
69
tenGradientCheck(const Nrrd *ngrad, int type, unsigned int minnum) {
70
  static const char me[]="tenGradientCheck";
71
72
128
  if (nrrdCheck(ngrad)) {
73
    biffMovef(TEN, NRRD, "%s: basic validity check failed", me);
74
    return 1;
75
  }
76

128
  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

72
  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
64
  if (nrrdTypeBlock == ngrad->type) {
88
    biffAddf(TEN, "%s: sorry, can't use %s type", me,
89
             airEnumStr(nrrdType, nrrdTypeBlock));
90
    return 1;
91
  }
92
64
  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
64
  return 0;
100
64
}
101
102
/*
103
******** tenGradientRandom
104
**
105
** generates num random unit vectors of type double
106
*/
107
int
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
8
  if (nrrdMaybeAlloc_va(ngrad, nrrdTypeDouble, 2,
114
16
                        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
8
  airSrandMT(seed);
119
8
  grad = AIR_CAST(double*, ngrad->data);
120
176
  for (gi=0; gi<num; gi++) {
121
    do {
122
160
      grad[0] = AIR_AFFINE(0, airDrandMT(), 1, -1, 1);
123
160
      grad[1] = AIR_AFFINE(0, airDrandMT(), 1, -1, 1);
124
160
      grad[2] = AIR_AFFINE(0, airDrandMT(), 1, -1, 1);
125
160
      len = ELL_3V_LEN(grad);
126

240
    } while (len > 1 || !len);
127
80
    ELL_3V_SCALE(grad, 1.0/len, grad);
128
80
    grad += 3;
129
  }
130
8
  return 0;
131
8
}
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
141
tenGradientIdealEdge(unsigned int N, int single) {
142
143
15312
  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
154
tenGradientJitter(Nrrd *nout, const Nrrd *nin, double dist) {
155
  static const char me[]="tenGradientJitter";
156
16
  double *grad, perp0[3], perp1[3], len, theta, cc, ss, edge;
157
  unsigned int gi, num;
158
159
8
  if (nrrdConvert(nout, nin, nrrdTypeDouble)) {
160
    biffMovef(TEN, NRRD, "%s: trouble converting input to double", me);
161
    return 1;
162
  }
163
8
  if (tenGradientCheck(nout, nrrdTypeDouble, 3)) {
164
    biffAddf(TEN, "%s: didn't get valid gradients", me);
165
    return 1;
166
  }
167
8
  grad = AIR_CAST(double*, nout->data);
168
8
  num = AIR_UINT(nout->axis[1].size);
169
  /* HEY: possible confusion between single and not */
170
8
  edge = tenGradientIdealEdge(num, AIR_FALSE);
171
176
  for (gi=0; gi<num; gi++) {
172
80
    ELL_3V_NORM(grad, grad, len);
173
80
    ell_3v_perp_d(perp0, grad);
174
80
    ELL_3V_CROSS(perp1, perp0, grad);
175
80
    theta = AIR_AFFINE(0, airDrandMT(), 1, 0, 2*AIR_PI);
176
80
    cc = dist*edge*cos(theta);
177
80
    ss = dist*edge*sin(theta);
178
80
    ELL_3V_SCALE_ADD3(grad, 1.0, grad, cc, perp0, ss, perp1);
179
80
    ELL_3V_NORM(grad, grad, len);
180
80
    grad += 3;
181
  }
182
183
8
  return 0;
184
8
}
185
186
void
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
7680
  if (!(pot && npos && tgparm )) {
196
    return;
197
  }
198
199
3840
  num = AIR_UINT(npos->axis[1].size);
200
3840
  pos = AIR_CAST(double *, npos->data);
201
11512
  edge = (edgeNormalize
202
3832
          ? tenGradientIdealEdge(num, tgparm->single)
203
          : 1.0);
204
3840
  *pot = 0;
205
3840
  if (minAngle) {
206
3832
    *minAngle = AIR_PI;
207
3832
  }
208
3840
  if (minEdge) {
209
8
    *minEdge = 2;
210
8
  }
211
84480
  for (ii=0; ii<num; ii++) {
212
422400
    for (jj=0; jj<ii; jj++) {
213
172800
      ELL_3V_SUB(diff, pos + 3*ii, pos + 3*jj);
214
172800
      len = ELL_3V_LEN(diff);
215
172800
      if (minEdge) {
216
1080
        *minEdge = AIR_MIN(*minEdge, len);
217
360
      }
218

345600
      if (tgparm->expo) {
219
345600
        ptmp = airIntPow(edge/len, tgparm->expo);
220
172800
      } else {
221
        ptmp = pow(edge/len, tgparm->expo_d);
222
      }
223
172800
      *pot += ptmp;
224
172800
      if (minAngle) {
225
172440
        atmp = ell_3v_angle_d(pos + 3*ii, pos + 3*jj);
226
517320
        *minAngle = AIR_MIN(atmp, *minAngle);
227
172440
      }
228
172800
      if (!tgparm->single) {
229
172800
        *pot += ptmp;
230
172800
        ELL_3V_ADD2(diff, pos + 3*ii, pos + 3*jj);
231
172800
        len = ELL_3V_LEN(diff);
232
172800
        if (minEdge) {
233
1080
          *minEdge = AIR_MIN(*minEdge, len);
234
360
        }
235

345600
        if (tgparm->expo) {
236
345600
          *pot += 2*airIntPow(edge/len, tgparm->expo);
237
172800
        } else {
238
          *pot += 2*pow(edge/len, tgparm->expo_d);
239
        }
240
172800
        if (minAngle) {
241
517320
          *minAngle = AIR_MIN(AIR_PI-atmp, *minAngle);
242
172440
        }
243
      }
244
    }
245
  }
246
3840
  return;
247
3840
}
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
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
7632
  pos = AIR_CAST(double *, npos->data);
290
3816
  num = AIR_UINT(npos->axis[1].size);
291
3816
  *meanVel = 0;
292
3816
  *edgeMin = edge;
293
11448
  expo = tgparm->expo ? tgparm->expo : tgparm->expo_d;
294
11448
  limit = expo*AIR_MIN(sqrt(expo),
295
                       log(1 + tgparm->initStep/tgparm->step));
296
83952
  for (ii=0; ii<num; ii++) {
297
    ELL_3V_SET(grad, 0, 0, 0);
298
839520
    for (jj=0; jj<num; jj++) {
299
381600
      if (ii == jj) {
300
        continue;
301
      }
302
343440
      ELL_3V_SUB(dir, pos + 3*ii, pos + 3*jj);
303
343440
      ELL_3V_NORM(dir, dir, len);
304
1030320
      *edgeMin = AIR_MIN(*edgeMin, len);
305

686880
      if (tgparm->expo) {
306
686880
        rep = airIntPow(edge/len, tgparm->expo+1);
307
343440
      } else {
308
        rep = pow(edge/len, tgparm->expo_d+1);
309
      }
310
343440
      ELL_3V_SCALE_INCR(grad, rep/num, dir);
311
343440
      if (!tgparm->single) {
312
343440
        ELL_3V_ADD2(dir, pos + 3*ii, pos + 3*jj);
313
343440
        ELL_3V_NORM(dir, dir, len);
314
1030320
        *edgeMin = AIR_MIN(*edgeMin, len);
315

686880
        if (tgparm->expo) {
316
686880
          rep = airIntPow(edge/len, tgparm->expo+1);
317
343440
        } else {
318
          rep = pow(edge/len, tgparm->expo_d+1);
319
        }
320
343440
        ELL_3V_SCALE_INCR(grad, rep/num, dir);
321
343440
      }
322
    }
323
38160
    ELL_3V_NORM(ngrad, grad, len);
324
38160
    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
38160
    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
114480
    step = AIR_MIN(len*tgparm->step, edge/limit);
337
38160
    ELL_3V_SCALE_ADD2(newpos,
338
                      1.0, pos + 3*ii,
339
                      step, ngrad);
340
38160
    ELL_3V_NORM(newpos, newpos, len);
341
38160
    ELL_3V_SUB(diff, pos + 3*ii, newpos);
342
38160
    *meanVel += ELL_3V_LEN(diff);
343
38160
    ELL_3V_COPY(pos + 3*ii, newpos);
344
  }
345
3816
  *meanVel /= num;
346
347
3816
  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
355
party(Nrrd *npos, airRandMTState *rstate) {
356
  double *pos, mean[3];
357
  unsigned int ii, num, rnd, rndBit;
358
359
560
  pos = (double *)(npos->data);
360
280
  num = AIR_UINT(npos->axis[1].size);
361
280
  rnd = airUIrandMT_r(rstate);
362
  rndBit = 0;
363
  ELL_3V_SET(mean, 0, 0, 0);
364
6160
  for (ii=0; ii<num; ii++) {
365
2800
    if (32 == rndBit) {
366
      rnd = airUIrandMT_r(rstate);
367
      rndBit = 0;
368
    }
369
2800
    if (rnd & (1 << rndBit++)) {
370
1384
      ELL_3V_SCALE(pos + 3*ii, -1, pos + 3*ii);
371
1384
    }
372
2800
    ELL_3V_INCR(mean, pos + 3*ii);
373
  }
374
280
  ELL_3V_SCALE(mean, 1.0/num, mean);
375
280
  return ELL_3V_LEN(mean);
376
}
377
378
/*
379
** parties until the gradients settle down
380
*/
381
int
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

24
  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
8
  if (nrrdConvert(nout, nin, nrrdTypeDouble)) {
398
    biffMovef(TEN, NRRD, "%s: can't initialize output with input", me);
399
    return 1;
400
  }
401
402
8
  mop = airMopNew();
403
8
  ncopy = nrrdNew();
404
8
  airMopAdd(mop, ncopy, (airMopper)nrrdNuke, airMopAlways);
405
8
  rstate = airRandMTStateNew(tgparm->seed);
406
8
  airMopAdd(mop, rstate, (airMopper)airRandMTStateNix, airMopAlways);
407
  /* HEY: factor of 100 is an approximate hack */
408
8
  maxIter = 100*tgparm->maxIteration;
409
410
  lastLen = 1.0;
411
  done = AIR_FALSE;
412
8
  do {
413
    iter = 0;
414
40
    do {
415
280
      iter++;
416
280
      len = party(nout, rstate);
417

520
    } while (len > lastLen && iter < maxIter);
418
40
    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
40
      if (nrrdCopy(ncopy, nout)) {
429
        biffMovef(TEN, NRRD, "%s: trouble copying", me);
430
        airMopError(mop); return 1;
431
      }
432
40
      improv = lastLen - len;
433
      lastLen = len;
434
40
      if (tgparm->verbose) {
435
        fprintf(stderr, "%s: (iter %u) improvement: %g  (mean length = %g)\n",
436
                me, iter, improv, len);
437
      }
438
40
      done = (improv <= tgparm->minMeanImprovement
439
112
              || len < tgparm->minMean);
440
    }
441
40
  } while (!done);
442
443
8
  airMopOkay(mop);
444
8
  return 0;
445
8
}
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
467
tenGradientDistribute(Nrrd *nout, const Nrrd *nin,
468
                      tenGradientParm *tgparm) {
469
  static const char me[]="tenGradientDistribute";
470
16
  char filename[AIR_STRLEN_SMALL];
471
  unsigned int ii, num, iter, oldIdx, newIdx, edgeShrink;
472
  airArray *mop;
473
8
  Nrrd *npos[2];
474
8
  double *pos, len, meanVelocity, pot, potNew, potD,
475
    edge, edgeMin, angle, angleNew;
476
  int E;
477
478

16
  if (!nout || tenGradientCheck(nin, nrrdTypeUnknown, 2) || !tgparm) {
479
    biffAddf(TEN, "%s: got NULL pointer or invalid input", me);
480
    return 1;
481
  }
482
483
8
  num = AIR_UINT(nin->axis[1].size);
484
8
  mop = airMopNew();
485
8
  npos[0] = nrrdNew();
486
8
  npos[1] = nrrdNew();
487
8
  airMopAdd(mop, npos[0], (airMopper)nrrdNuke, airMopAlways);
488
8
  airMopAdd(mop, npos[1], (airMopper)nrrdNuke, airMopAlways);
489
16
  if (nrrdConvert(npos[0], nin, nrrdTypeDouble)
490
16
      || nrrdConvert(npos[1], nin, nrrdTypeDouble)) {
491
    biffMovef(TEN, NRRD, "%s: trouble allocating temp buffers", me);
492
    airMopError(mop); return 1;
493
  }
494
495
8
  pos = (double*)(npos[0]->data);
496
176
  for (ii=0; ii<num; ii++) {
497
80
    ELL_3V_NORM(pos, pos, len);
498
80
    pos += 3;
499
  }
500
8
  if (tgparm->jitter) {
501
8
    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
8
  meanVelocity = 2*tgparm->minVelocity;
510
8
  potD = -2*tgparm->minPotentialChange;
511
  oldIdx = 0;
512
  newIdx = 1;
513
8
  tgparm->step = tgparm->initStep;
514
8
  tgparm->nudge = 0.1;
515
8
  tenGradientMeasure(&pot, &angle, NULL,
516
8
                     npos[oldIdx], tgparm, AIR_TRUE);
517
7640
  for (iter = 0;
518
3824
       ((!!tgparm->minIteration && iter < tgparm->minIteration)
519
        ||
520
3824
        (iter < tgparm->maxIteration
521
7648
         && (!tgparm->minPotentialChange
522
7648
             || !AIR_EXISTS(potD)
523
7648
             || -potD > tgparm->minPotentialChange)
524
7640
         && (!tgparm->minVelocity
525
7632
             || meanVelocity > tgparm->minVelocity)
526
7632
         && tgparm->step > FLT_MIN));
527
3816
       iter++) {
528
    /* copy positions from old to new */
529
3816
    memcpy(npos[newIdx]->data, npos[oldIdx]->data, 3*num*sizeof(double));
530
3816
    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
3816
    do {
536
3816
      E = _tenGradientUpdate(&meanVelocity, &edgeMin,
537
3816
                             npos[newIdx], edge, tgparm);
538
3816
      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
3816
    } while (E);
550
3816
    tenGradientMeasure(&potNew, &angleNew, NULL,
551
3816
                       npos[newIdx], tgparm, AIR_TRUE);
552

11448
    if ((AIR_EXISTS(pot) && AIR_EXISTS(potNew) && potNew <= pot)
553
3816
        || angleNew >= angle) {
554
      /* there was progress of some kind, either through potential
555
         decrease, or angle increase */
556
3816
      potD = 2*(potNew - pot)/(potNew + pot);
557

3832
      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

3816
      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
3816
      tgparm->step *= 1 + tgparm->nudge;
582
11448
      tgparm->step = AIR_MIN(tgparm->initStep, tgparm->step);
583
3816
      pot = potNew;
584
3816
      angle = angleNew;
585
      /* swap buffers */
586
3816
      newIdx = 1 - newIdx;
587
3816
      oldIdx = 1 - oldIdx;
588
3816
    } 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
8
  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
8
  tenGradientMeasure(&pot, NULL, NULL, npos[oldIdx], tgparm, AIR_FALSE);
625
8
  tgparm->potential = pot;
626
8
  tenGradientMeasure(&pot, &angle, &edge, npos[oldIdx], tgparm, AIR_TRUE);
627
8
  tgparm->potentialNorm = pot;
628
8
  tgparm->angle = angle;
629
8
  tgparm->edge = edge;
630
8
  tgparm->itersUsed = iter;
631
632

16
  if ((tgparm->minMeanImprovement || tgparm->minMean)
633
8
      && !tgparm->single) {
634
8
    if (tgparm->verbose) {
635
      fprintf(stderr, "%s: optimizing balance:\n", me);
636
    }
637
8
    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
8
    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
8
  airMopOkay(mop);
655
8
  return 0;
656
8
}
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
663
tenGradientGenerate(Nrrd *nout, unsigned int num, tenGradientParm *tgparm) {
664
  static const char me[]="tenGradientGenerate";
665
  Nrrd *nin;
666
  airArray *mop;
667
668
16
  if (!(nout && tgparm)) {
669
    biffAddf(TEN, "%s: got NULL pointer", me);
670
    return 1;
671
  }
672
8
  if (!( num >= 3 )) {
673
    biffAddf(TEN, "%s: can generate minimum of 3 gradient directions "
674
            "(not %d)", me, num);
675
    return 1;
676
  }
677
8
  mop = airMopNew();
678
8
  nin = nrrdNew();
679
8
  airMopAdd(mop, nin, (airMopper)nrrdNuke, airMopAlways);
680
681
16
  if (tenGradientRandom(nin, num, tgparm->seed)
682
16
      || tenGradientDistribute(nout, nin, tgparm)) {
683
    biffAddf(TEN, "%s: trouble", me);
684
    airMopError(mop); return 1;
685
  }
686
8
  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
8
    ptrdiff_t padMin[2] = {0, -1}, padMax[2];
692
8
    padMax[0] = AIR_CAST(ptrdiff_t, nout->axis[0].size-1);
693
8
    padMax[1] = AIR_CAST(ptrdiff_t, num-1);
694
8
    ntmp = nrrdNew();
695
8
    airMopAdd(mop, ntmp, (airMopper)nrrdNuke, airMopAlways);
696
16
    if (nrrdPad_nva(ntmp, nout, padMin, padMax,
697
                    nrrdBoundaryPad, 0.0)
698
16
        || nrrdCopy(nout, ntmp)) {
699
      biffMovef(TEN, NRRD, "%s: trouble adding zero vector", me);
700
      airMopError(mop); return 1;
701
    }
702
16
  }
703
704
8
  airMopOkay(mop);
705
8
  return 0;
706
8
}