GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: Testing/gage/probePolynomial.c Lines: 139 150 92.7 %
Date: 2017-05-26 Branches: 62 80 77.5 %

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 "teem/gage.h"
25
26
#define PROBE "probePolynomial"
27
28
/*
29
** Tests:
30
**
31
*/
32
33
/* the hope is that without too much work it would be possible to
34
   increase this to 4th-order polynomials and 4th-order derivatives */
35
#define POWER_MAX 3
36
37
/*
38
** polyeval takes a polynomial defined by coef --
39
** coef[i][j][k] is the coefficient for (x^i)*(y^j)*(z^k) --
40
** and evaluates at position pos[] the dx-th derivative along X,
41
** the dy-th derivative along Y, and the dz-th derivative along Z,
42
** dx = dy = dz = 0 to evaluate the polynomial itself
43
*/
44
static double
45
polyeval(double coef[POWER_MAX+1][POWER_MAX+1][POWER_MAX+1],
46
         unsigned int dx, unsigned int dy, unsigned int dz,
47
         const double pos[3]) {
48
  unsigned int xi, yi, zi;
49
28597632
  double tmp, ret,
50
    xp[POWER_MAX+1], yp[POWER_MAX+1], zp[POWER_MAX+1],
51
14298816
    dc[POWER_MAX+1][POWER_MAX+1] = {
52
    {1.0, 1.0, 1.0, 1.0},  /* how coeffs are scaled for 0th derivative */
53
    {0.0, 1.0, 2.0, 3.0},  /* how coeffs are scaled for 1st derivative */
54
    {0.0, 0.0, 2.0, 6.0},  /* how coeffs are scaled for 2nd derivative */
55
    {0.0, 0.0, 0.0, 6.0}}; /* how coeffs are scaled for 3rd derivative */
56
57
  tmp = 1.0;
58
142988160
  for (xi=0; xi<=POWER_MAX; xi++) {
59
57195264
    xp[xi] = tmp;
60
57195264
    tmp *= pos[0];
61
  }
62
  tmp = 1.0;
63
142988160
  for (yi=0; yi<=POWER_MAX; yi++) {
64
57195264
    yp[yi] = tmp;
65
57195264
    tmp *= pos[1];
66
  }
67
  tmp = 1.0;
68
142988160
  for (zi=0; zi<=POWER_MAX; zi++) {
69
57195264
    zp[zi] = tmp;
70
57195264
    tmp *= pos[2];
71
  }
72
73
  ret = 0.0;
74
142976810
  for (xi=dx; xi<=POWER_MAX; xi++) {
75
571852760
    for (yi=dy; yi<=POWER_MAX; yi++) {
76
2287204470
      for (zi=dz; zi<=POWER_MAX; zi++) {
77
1829730888
        ret += (coef[xi][yi][zi]*xp[xi-dx]*yp[yi-dy]*zp[zi-dz]
78
914865444
                *dc[dx][xi]*dc[dy][yi]*dc[dz][zi]);
79
      }
80
    }
81
  }
82
83
14298816
  return ret;
84
14298816
}
85
86
static void
87
randvec(double vec[NRRD_SPACE_DIM_MAX], double lenexp, double lensig,
88
        airRandMTState *rng) {
89
224
  double tmp, clen[2], len;
90
91
112
  airNormalRand_r(vec + 0, vec + 1, rng);
92
112
  airNormalRand_r(vec + 2, NULL, rng);
93
112
  ELL_3V_NORM(vec, vec, tmp);
94
112
  airNormalRand_r(clen + 0, clen + 1, rng);
95
  /* rician noise, actually */
96
112
  clen[0] = lenexp + lensig*clen[0];
97
112
  clen[1] = lensig*clen[1];
98
112
  len = ELL_2V_LEN(clen);
99
112
  ELL_3V_SCALE(vec, len, vec);
100
112
}
101
102
/*
103
** makeVolume allocates inside nin a new randomly oriented volume of
104
** size sx-by-sy-sz; the spaceDirection vectors are random but enforced
105
** to not be too parallel
106
*/
107
static gageContext *
108
makeVolume(Nrrd *nin, unsigned int sx, unsigned int sy, unsigned int sz,
109
           double coef[POWER_MAX+1][POWER_MAX+1][POWER_MAX+1],
110
           airRandMTState *rng) {
111
  static const char me[]="makeVolume";
112
60
  double spcOrig[NRRD_SPACE_DIM_MAX], spcVec[3][NRRD_SPACE_DIM_MAX],
113
    angle10, angle20, angle21, ooff[3], aperm=0.6, lexp=0.1, lstdv=0.04,
114
    kparm[NRRD_KERNEL_PARMS_NUM];
115
  gageContext *gctx;
116
  gagePerVolume *gpvl;
117
  unsigned int xi, yi, zi;
118
  airArray *submop;
119
  int EE;
120
121
30
  submop = airMopNew();
122
30
  randvec(spcVec[0], lexp, lstdv, rng);
123
30
  do {
124
32
    randvec(spcVec[1], lexp, lstdv, rng);
125
32
    angle10 = ell_3v_angle_d(spcVec[1], spcVec[0]);
126

95
  } while (!( AIR_IN_OP(AIR_PI*(1-aperm)/2, angle10, AIR_PI*(1+aperm)/2) ));
127
  do {
128
50
    randvec(spcVec[2], lexp, lstdv, rng);
129
50
    angle20 = ell_3v_angle_d(spcVec[2], spcVec[0]);
130
50
    angle21 = ell_3v_angle_d(spcVec[2], spcVec[1]);
131

146
  } while (!( AIR_IN_OP(AIR_PI*(1-aperm)/2, angle20, AIR_PI*(1+aperm)/2) &&
132
76
              AIR_IN_OP(AIR_PI*(1-aperm)/2, angle21, AIR_PI*(1+aperm)/2) ));
133
134
  /* center volume near origin */
135
30
  airNormalRand_r(ooff + 0, ooff + 1, rng);
136
30
  airNormalRand_r(ooff + 2, NULL, rng);
137
30
  ELL_3V_SET(spcOrig, 0.0, 0.0, 0.0);
138
30
  ELL_3V_SCALE_INCR(spcOrig, -AIR_CAST(double, sx)/2.0 + 2*ooff[0], spcVec[0]);
139
30
  ELL_3V_SCALE_INCR(spcOrig, -AIR_CAST(double, sy)/2.0 + 2*ooff[1], spcVec[1]);
140
30
  ELL_3V_SCALE_INCR(spcOrig, -AIR_CAST(double, sz)/2.0 + 2*ooff[1], spcVec[2]);
141
142
  /* allocate data */
143
60
  if (nrrdMaybeAlloc_va(nin, nrrdTypeDouble, 3,
144
30
                        AIR_CAST(size_t, sx),
145
30
                        AIR_CAST(size_t, sy),
146
30
                        AIR_CAST(size_t, sz))
147
60
      || nrrdSpaceSet(nin, nrrdSpaceRightAnteriorSuperior)
148
60
      || nrrdSpaceOriginSet(nin, spcOrig)) {
149
    biffMovef(PROBE, NRRD, "%s: trouble setting volume", me);
150
    airMopError(submop); return NULL;
151
  }
152
30
  nrrdAxisInfoSet_va(nin, nrrdAxisInfoSpaceDirection,
153
                     spcVec[0],
154
                     spcVec[1],
155
                     spcVec[2]);
156
30
  nrrdAxisInfoSet_va(nin, nrrdAxisInfoCenter,
157
                     nrrdCenterCell,
158
                     nrrdCenterCell,
159
                     nrrdCenterCell);
160
161
  /* set data */
162
  {
163
30
    double *ddata = AIR_CAST(double *, nin->data);
164
5236
    for (zi=0; zi<sz; zi++) {
165
2588
      double pos[3];
166
405998
      for (yi=0; yi<sy; yi++) {
167
28975754
        for (xi=0; xi<sx; xi++) {
168
14287466
          ELL_3V_SCALE_ADD2(pos, 1.0, spcOrig, xi, spcVec[0]);
169
14287466
          ELL_3V_SCALE_ADD2(pos, 1.0, pos, yi, spcVec[1]);
170
14287466
          ELL_3V_SCALE_ADD2(pos, 1.0, pos, zi, spcVec[2]);
171
14287466
          ddata[xi + sx*(yi + sy*zi)] = polyeval(coef, 0, 0, 0, pos);
172
        }
173
      }
174
2588
    }
175
  }
176
177
  /* create context, using the c4hexic kernel and its derivatives.  c4hexic
178
     is can reconstruct cubics without pre-filtering, and its analytical
179
     derivatives are readily available. tmf:n,3,4 can also reconstruct
180
     cubics but its not clear which other kernels are its derivatives */
181
30
  gctx = gageContextNew();
182
30
  airMopAdd(submop, gctx, (airMopper)gageContextNix, airMopOnError);
183
30
  gageParmSet(gctx, gageParmRenormalize, AIR_FALSE);
184
30
  gageParmSet(gctx, gageParmCheckIntegrals, AIR_TRUE);
185
  EE = 0;
186
60
  if (!EE) EE |= gageKernelSet(gctx, gageKernel00, nrrdKernelC4Hexic, kparm);
187
60
  if (!EE) EE |= gageKernelSet(gctx, gageKernel11, nrrdKernelC4HexicD, kparm);
188
60
  if (!EE) EE |= gageKernelSet(gctx, gageKernel22, nrrdKernelC4HexicDD, kparm);
189
60
  if (!EE) EE |= !(gpvl = gagePerVolumeNew(gctx, nin, gageKindScl));
190
60
  if (!EE) EE |= gagePerVolumeAttach(gctx, gpvl);
191
60
  if (!EE) EE |= gageQueryItemOn(gctx, gpvl, gageSclValue);
192
60
  if (!EE) EE |= gageQueryItemOn(gctx, gpvl, gageSclGradVec);
193
60
  if (!EE) EE |= gageQueryItemOn(gctx, gpvl, gageSclHessian);
194
60
  if (!EE) EE |= gageUpdate(gctx);
195
30
  if (EE) {
196
    biffMovef(PROBE, GAGE, "%s: trouble setting up gage", me);
197
    airMopError(submop); return NULL;
198
  }
199
200
30
  airMopOkay(submop);
201
30
  return gctx;
202
30
}
203
204
int
205
main() {
206
  airArray *mop;
207
  char *err;
208
209
  airRandMTState *rng;
210
  Nrrd *nin;
211
  unsigned int xi, yi, zi, runNum, runIdx;
212
2
  double coef[POWER_MAX+1][POWER_MAX+1][POWER_MAX+1], pos[3], epsilon;
213
  const double *vmeas, *gmeas, *hmeas;
214
  gageContext *gctx;
215
216
1
  mop = airMopNew();
217
218
1
  rng = airRandMTStateNew(429);
219
1
  airMopAdd(mop, rng, (airMopper)airRandMTStateNix, airMopAlways);
220
221
1
  nin = nrrdNew();
222
1
  airMopAdd(mop, nin, (airMopper)nrrdNuke, airMopAlways);
223
  epsilon = 1e-8;
224
  runNum = 30;
225
226
62
  for (runIdx=0; runIdx<runNum; runIdx++) {
227
    unsigned int sx, sy, sz, probeNum;
228
    double avgDiff;
229
    airArray *submop;
230
231
30
    submop = airMopNew();
232
    /* set random coefficients in polynomial */
233
300
    for (xi=0; xi<=POWER_MAX; xi++) {
234
1200
      for (yi=0; yi<=POWER_MAX; yi++) {
235
4800
        for (zi=0; zi<=POWER_MAX; zi++) {
236

3840
          if (xi + yi + zi > POWER_MAX) {
237
3240
            coef[xi][yi][zi] = 0.0;
238
1320
          } else {
239
600
            airNormalRand_r(&(coef[xi][yi][zi]), NULL, rng);
240
          }
241
        }
242
      }
243
    }
244
245
30
    sx = 20 + airRandInt_r(rng, 120);
246
30
    sy = 20 + airRandInt_r(rng, 120);
247
30
    sz = 20 + airRandInt_r(rng, 120);
248
30
    fprintf(stderr, "%u: %u %u %u: ", runIdx, sx, sy, sz); fflush(stderr);
249
30
    if (!(gctx = makeVolume(nin, sx, sy, sz, coef, rng))) {
250
      airMopAdd(mop, err = biffGetDone(PROBE), airFree, airMopAlways);
251
      fprintf(stderr, "trouble creating volume:\n%s", err);
252
      airMopError(submop); airMopError(mop); return 1;
253
    }
254
30
    airMopAdd(submop, gctx, (airMopper)gageContextNix, airMopAlways);
255
30
    vmeas = gageAnswerPointer(gctx, gctx->pvl[0], gageSclValue);
256
30
    gmeas = gageAnswerPointer(gctx, gctx->pvl[0], gageSclGradVec);
257
30
    hmeas = gageAnswerPointer(gctx, gctx->pvl[0], gageSclHessian);
258
30
    ELL_3V_SET(pos, 0, 0, 0);
259
30
    gageProbeSpace(gctx, pos[0], pos[1], pos[2],
260
                   AIR_FALSE /* indexSpace */,
261
                   AIR_TRUE /* clamp */);
262
    probeNum = 0;
263
    avgDiff = 0.0;
264
    /* take a random walk starting at (0,0,0), making sure that at the
265
       visited positions the gage-measured values, gradients, and
266
       hessians are the same as the analytical ones */
267
30
    do {
268
1135
      double vanal, ganal[3], hanal[9], vdiff, gdiff[3], hdiff[9],
269
        step[3], stepSize = 0.2;
270
1135
      probeNum++;
271
1135
      vanal = polyeval(coef, 0, 0, 0, pos);
272
1135
      ganal[0] = polyeval(coef, 1, 0, 0, pos);
273
1135
      ganal[1] = polyeval(coef, 0, 1, 0, pos);
274
1135
      ganal[2] = polyeval(coef, 0, 0, 1, pos);
275
1135
      hanal[0] = polyeval(coef, 2, 0, 0, pos);
276
1135
      hanal[1] = polyeval(coef, 1, 1, 0, pos);
277
1135
      hanal[2] = polyeval(coef, 1, 0, 1, pos);
278
      hanal[3] = hanal[1];
279
1135
      hanal[4] = polyeval(coef, 0, 2, 0, pos);
280
1135
      hanal[5] = polyeval(coef, 0, 1, 1, pos);
281
      hanal[6] = hanal[2];
282
      hanal[7] = hanal[5];
283
1135
      hanal[8] = polyeval(coef, 0, 0, 2, pos);
284
1135
      vdiff = fabs(vmeas[0] - vanal);
285
1135
      ELL_3V_SUB(gdiff, gmeas, ganal);
286
1135
      ELL_3M_SUB(hdiff, hmeas, hanal);
287

2270
      if (vdiff > epsilon ||
288
1135
          ELL_3V_LEN(gdiff) > epsilon ||
289
1135
          ELL_3M_FROB(hdiff) > epsilon) {
290
        fprintf(stderr, "at (%g,%g,%g) one diff %.17g %.17g %.17g "
291
                "> eps %.17g\n",
292
                pos[0], pos[1], pos[2], vdiff,
293
                ELL_3V_LEN(gdiff), ELL_3M_FROB(hdiff), epsilon);
294
        airMopError(submop); airMopError(mop); return 1;
295
      }
296
1135
      avgDiff += vdiff + ELL_3V_LEN(gdiff) + ELL_3M_FROB(hdiff);
297
1135
      airNormalRand_r(step + 0, step + 1, rng);
298
1135
      airNormalRand_r(step + 2, NULL, rng);
299
1135
      ELL_3V_SCALE_INCR(pos, stepSize, step);
300
1135
      gageProbeSpace(gctx, pos[0], pos[1], pos[2],
301
                     AIR_FALSE /* indexSpace */,
302
                     AIR_TRUE /* clamp */);
303

3405
    } while (!gctx->edgeFrac);
304
30
    avgDiff /= probeNum;
305
30
    fprintf(stderr, "%u (%.17g)\n", probeNum, avgDiff);
306
30
    airMopOkay(submop);
307
30
  }
308
309
1
  airMopOkay(mop);
310
1
  return 0;
311
1
}