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 |
|
|
|
25 |
|
|
#include "pull.h" |
26 |
|
|
#include "privatePull.h" |
27 |
|
|
|
28 |
|
|
/* |
29 |
|
|
** issues: |
30 |
|
|
** does everything work on the first iteration |
31 |
|
|
** how to handle the needed extra probe for d strength / d scale |
32 |
|
|
** how are force/energy along scale handled differently than in space? |
33 |
|
|
*/ |
34 |
|
|
|
35 |
|
|
#define __IF_DEBUG if (0) |
36 |
|
|
|
37 |
|
|
static double |
38 |
|
|
_pointDistSqrd(pullContext *pctx, pullPoint *AA, pullPoint *BB) { |
39 |
|
|
double diff[4]; |
40 |
|
|
ELL_4V_SUB(diff, AA->pos, BB->pos); |
41 |
|
|
ELL_3V_SCALE(diff, 1/pctx->sysParm.radiusSpace, diff); |
42 |
|
|
diff[3] /= pctx->sysParm.radiusScale; |
43 |
|
|
return ELL_4V_DOT(diff, diff); |
44 |
|
|
} |
45 |
|
|
|
46 |
|
|
/* |
47 |
|
|
** this sets, in task->neighPoint (*NOT* point->neighPoint), all the |
48 |
|
|
** points in neighboring bins with which we might possibly interact, |
49 |
|
|
** and returns the number of such points. |
50 |
|
|
*/ |
51 |
|
|
static unsigned int |
52 |
|
|
_neighBinPoints(pullTask *task, pullBin *bin, pullPoint *point, |
53 |
|
|
double distTest) { |
54 |
|
|
static const char me[]="_neighBinPoints"; |
55 |
|
|
unsigned int nn, herPointIdx, herBinIdx; |
56 |
|
|
pullBin *herBin; |
57 |
|
|
pullPoint *herPoint; |
58 |
|
|
|
59 |
|
|
nn = 0; |
60 |
|
|
herBinIdx = 0; |
61 |
|
|
while ((herBin = bin->neighBin[herBinIdx])) { |
62 |
|
|
for (herPointIdx=0; herPointIdx<herBin->pointNum; herPointIdx++) { |
63 |
|
|
herPoint = herBin->point[herPointIdx]; |
64 |
|
|
/* |
65 |
|
|
printf("!%s(%u): neighbin %u has point %u\n", me, |
66 |
|
|
point->idtag, herBinIdx, herPoint->idtag); |
67 |
|
|
*/ |
68 |
|
|
/* can't interact with myself, or anything nixed */ |
69 |
|
|
if (point != herPoint |
70 |
|
|
&& !(herPoint->status & PULL_STATUS_NIXME_BIT)) { |
71 |
|
|
if (distTest |
72 |
|
|
&& _pointDistSqrd(task->pctx, point, herPoint) > distTest) { |
73 |
|
|
continue; |
74 |
|
|
} |
75 |
|
|
if (nn+1 < _PULL_NEIGH_MAXNUM) { |
76 |
|
|
task->neighPoint[nn++] = herPoint; |
77 |
|
|
/* |
78 |
|
|
printf("%s(%u): neighPoint[%u] = %u\n", |
79 |
|
|
me, point->idtag, nn-1, herPoint->idtag); |
80 |
|
|
*/ |
81 |
|
|
} else { |
82 |
|
|
fprintf(stderr, "%s: hit max# (%u) poss. neighbors (from bins)\n", |
83 |
|
|
me, _PULL_NEIGH_MAXNUM); |
84 |
|
|
} |
85 |
|
|
} |
86 |
|
|
} |
87 |
|
|
herBinIdx++; |
88 |
|
|
} |
89 |
|
|
/* also have to consider things in the add queue */ |
90 |
|
|
for (herPointIdx=0; herPointIdx<task->addPointNum; herPointIdx++) { |
91 |
|
|
herPoint = task->addPoint[herPointIdx]; |
92 |
|
|
if (point != herPoint) { |
93 |
|
|
if (distTest |
94 |
|
|
&& _pointDistSqrd(task->pctx, point, herPoint) > distTest) { |
95 |
|
|
continue; |
96 |
|
|
} |
97 |
|
|
if (nn+1 < _PULL_NEIGH_MAXNUM) { |
98 |
|
|
task->neighPoint[nn++] = herPoint; |
99 |
|
|
} else { |
100 |
|
|
fprintf(stderr, "%s: hit max# (%u) poss neighs (add queue len %u)\n", |
101 |
|
|
me, _PULL_NEIGH_MAXNUM, task->addPointNum); |
102 |
|
|
} |
103 |
|
|
} |
104 |
|
|
} |
105 |
|
|
return nn; |
106 |
|
|
} |
107 |
|
|
|
108 |
|
|
/* |
109 |
|
|
** compute the energy at "me" due to "she", and |
110 |
|
|
** the gradient vector of her energy (probably pointing towards her) |
111 |
|
|
** |
112 |
|
|
** we're passed spaceDist to save us work of recomputing sqrt() |
113 |
|
|
** |
114 |
|
|
** egrad will be NULL if this is being called only to assess |
115 |
|
|
** the energy at this point, rather than for learning how to move it |
116 |
|
|
*/ |
117 |
|
|
double |
118 |
|
|
_pullEnergyInterParticle(pullContext *pctx, pullPoint *me, |
119 |
|
|
const pullPoint *she, |
120 |
|
|
double spaceDist, double scaleDist, |
121 |
|
|
/* output */ |
122 |
|
|
double egrad[4]) { |
123 |
|
|
char meme[]="pullEnergyInterParticle"; |
124 |
|
|
double diff[4], spaceRad, scaleRad, rr, ss, uu, beta, |
125 |
|
|
en, den, enR, denR, enS, denS, enWR, enWS, denWR, denWS, |
126 |
|
|
*parmR, *parmS, *parmW, |
127 |
|
|
(*evalR)(double *, double, const double parm[PULL_ENERGY_PARM_NUM]), |
128 |
|
|
(*evalS)(double *, double, const double parm[PULL_ENERGY_PARM_NUM]), |
129 |
|
|
(*evalW)(double *, double, const double parm[PULL_ENERGY_PARM_NUM]); |
130 |
|
|
int scaleSgn; |
131 |
|
|
|
132 |
|
|
/* the vector "diff" goes from her, to me, in both space and scale */ |
133 |
|
|
ELL_4V_SUB(diff, me->pos, she->pos); |
134 |
|
|
/* computed by caller: spaceDist = ELL_3V_LEN(diff); */ |
135 |
|
|
/* computed by caller: scaleDist = AIR_ABS(diff[3]); */ |
136 |
|
|
spaceRad = pctx->sysParm.radiusSpace; |
137 |
|
|
scaleRad = pctx->sysParm.radiusScale; |
138 |
|
|
rr = spaceDist/spaceRad; |
139 |
|
|
if (pctx->haveScale) { |
140 |
|
|
ss = scaleDist/scaleRad; |
141 |
|
|
scaleSgn = airSgn(diff[3]); |
142 |
|
|
} else { |
143 |
|
|
ss = 0; |
144 |
|
|
scaleSgn = 1; |
145 |
|
|
} |
146 |
|
|
if (rr > 1 || ss > 1) { |
147 |
|
|
if (egrad) { |
148 |
|
|
ELL_4V_SET(egrad, 0, 0, 0, 0); |
149 |
|
|
} |
150 |
|
|
return 0; |
151 |
|
|
} |
152 |
|
|
if (rr == 0 && ss == 0) { |
153 |
|
|
fprintf(stderr, "%s: pos(%u) == pos(%u) !! (%g,%g,%g,%g)\n", |
154 |
|
|
meme, me->idtag, she->idtag, |
155 |
|
|
me->pos[0], me->pos[1], me->pos[2], me->pos[3]); |
156 |
|
|
if (egrad) { |
157 |
|
|
ELL_4V_SET(egrad, 0, 0, 0, 0); |
158 |
|
|
} |
159 |
|
|
return 0; |
160 |
|
|
} |
161 |
|
|
#if PULL_HINTER |
162 |
|
|
if (pullProcessModeDescent == pctx->task[0]->processMode |
163 |
|
|
&& pctx->nhinter && pctx->nhinter->data) { |
164 |
|
|
unsigned int ri, si, sz; |
165 |
|
|
float *hint; |
166 |
|
|
hint = AIR_CAST(float *, pctx->nhinter->data); |
167 |
|
|
sz = pctx->nhinter->axis[0].size; |
168 |
|
|
ri = airIndex(-1.0, rr, 1.0, sz); |
169 |
|
|
si = airIndex(-1.0, ss*scaleSgn, 1.0, sz); |
170 |
|
|
hint[ri + sz*si] += 1; |
171 |
|
|
} |
172 |
|
|
#endif |
173 |
|
|
|
174 |
|
|
parmR = pctx->energySpecR->parm; |
175 |
|
|
evalR = pctx->energySpecR->energy->eval; |
176 |
|
|
parmS = pctx->energySpecS->parm; |
177 |
|
|
evalS = pctx->energySpecS->energy->eval; |
178 |
|
|
switch (pctx->interType) { |
179 |
|
|
case pullInterTypeJustR: |
180 |
|
|
/* _pullVolumeSetup makes sure that |
181 |
|
|
!pctx->haveScale iff pullInterTypeJustR == pctx->interType */ |
182 |
|
|
en = evalR(&denR, rr, parmR); |
183 |
|
|
if (egrad) { |
184 |
|
|
denR *= 1.0/(spaceRad*spaceDist); |
185 |
|
|
ELL_3V_SCALE(egrad, denR, diff); |
186 |
|
|
egrad[3] = 0; |
187 |
|
|
} |
188 |
|
|
break; |
189 |
|
|
case pullInterTypeUnivariate: |
190 |
|
|
uu = sqrt(rr*rr + ss*ss); |
191 |
|
|
en = evalR(&den, uu, parmR); |
192 |
|
|
if (egrad) { |
193 |
|
|
ELL_3V_SCALE(egrad, den/(uu*spaceRad*spaceRad), diff); |
194 |
|
|
egrad[3] = den*diff[3]/(uu*scaleRad*scaleRad); |
195 |
|
|
} |
196 |
|
|
break; |
197 |
|
|
case pullInterTypeSeparable: |
198 |
|
|
enR = evalR(&denR, rr, parmR); |
199 |
|
|
enS = evalS(&denS, ss, parmS); |
200 |
|
|
en = enR*enS; |
201 |
|
|
if (egrad) { |
202 |
|
|
ELL_3V_SCALE(egrad, denR*enS/(spaceRad*spaceDist), diff); |
203 |
|
|
egrad[3] = enR*airSgn(diff[3])*denS/scaleRad; |
204 |
|
|
} |
205 |
|
|
break; |
206 |
|
|
case pullInterTypeAdditive: |
207 |
|
|
parmW = pctx->energySpecWin->parm; |
208 |
|
|
evalW = pctx->energySpecWin->energy->eval; |
209 |
|
|
enR = evalR(&denR, rr, parmR); |
210 |
|
|
enS = evalS(&denS, ss, parmS); |
211 |
|
|
enWR = evalW(&denWR, rr, parmW); |
212 |
|
|
enWS = evalW(&denWS, ss, parmW); |
213 |
|
|
beta = pctx->sysParm.beta; |
214 |
|
|
en = AIR_LERP(beta, enR*enWS, enS*enWR); |
215 |
|
|
if (egrad) { |
216 |
|
|
double egradR[4], egradS[4]; |
217 |
|
|
ELL_3V_SCALE(egradR, denR*enWS/(spaceRad*spaceDist), diff); |
218 |
|
|
ELL_3V_SCALE(egradS, denWR*enS/(spaceRad*spaceDist), diff); |
219 |
|
|
egradR[3] = enR*scaleSgn*denWS/scaleRad; |
220 |
|
|
egradS[3] = enWR*scaleSgn*denS/scaleRad; |
221 |
|
|
ELL_4V_LERP(egrad, beta, egradR, egradS); |
222 |
|
|
} |
223 |
|
|
break; |
224 |
|
|
default: |
225 |
|
|
fprintf(stderr, "!%s: sorry, intertype %d unimplemented", meme, |
226 |
|
|
pctx->interType); |
227 |
|
|
en = AIR_NAN; |
228 |
|
|
if (egrad) { |
229 |
|
|
ELL_4V_SET(egrad, AIR_NAN, AIR_NAN, AIR_NAN, AIR_NAN); |
230 |
|
|
} |
231 |
|
|
break; |
232 |
|
|
} |
233 |
|
|
/* |
234 |
|
|
printf("%s: %u <-- %u = %g,%g,%g -> egrad = %g,%g,%g, enr = %g\n", |
235 |
|
|
meme, me->idtag, she->idtag, |
236 |
|
|
diff[0], diff[1], diff[2], |
237 |
|
|
egrad[0], egrad[1], egrad[2], enr); |
238 |
|
|
*/ |
239 |
|
|
return en; |
240 |
|
|
} |
241 |
|
|
|
242 |
|
|
int |
243 |
|
|
pullEnergyPlot(pullContext *pctx, Nrrd *nplot, |
244 |
|
|
double xx, double yy, double zz, |
245 |
|
|
unsigned int res) { |
246 |
|
|
static const char meme[]="pullEnergyPlot"; |
247 |
|
|
pullPoint *me, *she; |
248 |
|
|
airArray *mop; |
249 |
|
|
double dir[3], len, *plot, _rr, _ss, rr, ss, enr, egrad[4]; |
250 |
|
|
size_t size[3]; |
251 |
|
|
unsigned int ri, si; |
252 |
|
|
|
253 |
|
|
if (!( pctx && nplot )) { |
254 |
|
|
biffAddf(PULL, "%s: got NULL pointer", meme); |
255 |
|
|
return 1; |
256 |
|
|
} |
257 |
|
|
ELL_3V_SET(dir, xx, yy, zz); |
258 |
|
|
if (!ELL_3V_LEN(dir)) { |
259 |
|
|
biffAddf(PULL, "%s: need non-zero length dir", meme); |
260 |
|
|
return 1; |
261 |
|
|
} |
262 |
|
|
ELL_3V_NORM(dir, dir, len); |
263 |
|
|
ELL_3V_SET(size, 3, res, res); |
264 |
|
|
if (nrrdMaybeAlloc_nva(nplot, nrrdTypeDouble, 3, size)) { |
265 |
|
|
biffMovef(PULL, NRRD, "%s: trouble allocating output", meme); |
266 |
|
|
return 1; |
267 |
|
|
} |
268 |
|
|
|
269 |
|
|
mop = airMopNew(); |
270 |
|
|
me = pullPointNew(pctx); |
271 |
|
|
she = pullPointNew(pctx); |
272 |
|
|
airMopAdd(mop, me, (airMopper)pullPointNix, airMopAlways); |
273 |
|
|
airMopAdd(mop, she, (airMopper)pullPointNix, airMopAlways); |
274 |
|
|
ELL_4V_SET(me->pos, 0, 0, 0, 0); |
275 |
|
|
plot = AIR_CAST(double *, nplot->data); |
276 |
|
|
for (si=0; si<res; si++) { |
277 |
|
|
_ss = AIR_AFFINE(0, si, res-1, -1.0, 1.0); |
278 |
|
|
ss = _ss*pctx->sysParm.radiusScale; |
279 |
|
|
for (ri=0; ri<res; ri++) { |
280 |
|
|
_rr = AIR_AFFINE(0, ri, res-1, -1.0, 1.0); |
281 |
|
|
rr = _rr*pctx->sysParm.radiusSpace; |
282 |
|
|
ELL_3V_SCALE(she->pos, rr, dir); |
283 |
|
|
she->pos[3] = ss; |
284 |
|
|
enr = _pullEnergyInterParticle(pctx, me, she, |
285 |
|
|
AIR_ABS(rr), AIR_ABS(ss), egrad); |
286 |
|
|
plot[0] = enr; |
287 |
|
|
plot[1] = ELL_3V_DOT(egrad, dir); |
288 |
|
|
plot[2] = egrad[3]; |
289 |
|
|
plot += 3; |
290 |
|
|
} |
291 |
|
|
} |
292 |
|
|
|
293 |
|
|
airMopOkay(mop); |
294 |
|
|
return 0; |
295 |
|
|
} |
296 |
|
|
|
297 |
|
|
/* |
298 |
|
|
** computes energy from neighboring points. The non-NULLity of |
299 |
|
|
** "egradSum" determines the energy *gradient* is computed (with |
300 |
|
|
** possible constraint modifications) and stored there |
301 |
|
|
** |
302 |
|
|
** always computed: |
303 |
|
|
** point->neighInterNum |
304 |
|
|
** point->neighDistMean |
305 |
|
|
** |
306 |
|
|
** if pullProcessModeNeighLearn == task->processMode: |
307 |
|
|
** point->neighCovar |
308 |
|
|
** point->neighTanCovar |
309 |
|
|
** point->neighNum |
310 |
|
|
** |
311 |
|
|
** 0=0 1=1 2=2 3=3 |
312 |
|
|
** (4) 4=5 5=6 6=7 |
313 |
|
|
** (8) (9) 7=10 8=11 |
314 |
|
|
** (12) (13) (14) 9=15 |
315 |
|
|
*/ |
316 |
|
|
double |
317 |
|
|
_pullEnergyFromPoints(pullTask *task, pullBin *bin, pullPoint *point, |
318 |
|
|
/* output */ |
319 |
|
|
double egradSum[4]) { |
320 |
|
|
static const char me[]="_pullEnergyFromPoints"; |
321 |
|
|
double energySum, spaDistSqMax; /* modeWghtSum */ |
322 |
|
|
int nlist, /* we enable the re-use of neighbor lists between inters, or, |
323 |
|
|
at system start, creation of neighbor lists */ |
324 |
|
|
ntrue; /* we search all possible neighbors availble in the bins |
325 |
|
|
(either because !nlist, or, this iter we learn true |
326 |
|
|
subset of interacting neighbors). This could also |
327 |
|
|
be called "dontreuse" or something like that */ |
328 |
|
|
unsigned int nidx, |
329 |
|
|
nnum; /* how much of task->neigh[] we use */ |
330 |
|
|
|
331 |
|
|
/* set nlist and ntrue */ |
332 |
|
|
if (pullProcessModeNeighLearn == task->processMode) { |
333 |
|
|
/* we're here to both learn and store the true interacting neighbors */ |
334 |
|
|
nlist = AIR_TRUE; |
335 |
|
|
ntrue = AIR_TRUE; |
336 |
|
|
} else if (pullProcessModeAdding == task->processMode |
337 |
|
|
|| pullProcessModeNixing == task->processMode) { |
338 |
|
|
/* we assume that the work of learning neighbors has already been |
339 |
|
|
done, so we can reuse them now */ |
340 |
|
|
nlist = AIR_TRUE; |
341 |
|
|
ntrue = AIR_FALSE; |
342 |
|
|
} else if (pullProcessModeDescent == task->processMode) { |
343 |
|
|
if (task->pctx->sysParm.neighborTrueProb < 1) { |
344 |
|
|
nlist = AIR_TRUE; |
345 |
|
|
if (egradSum) { |
346 |
|
|
/* We allow the neighbor list optimization only when we're also |
347 |
|
|
asked to compute the energy gradient, since that's the first |
348 |
|
|
part of moving the particle. */ |
349 |
|
|
ntrue = (0 == task->pctx->iter |
350 |
|
|
|| (airDrandMT_r(task->rng) |
351 |
|
|
< task->pctx->sysParm.neighborTrueProb)); |
352 |
|
|
} else { |
353 |
|
|
/* When we're not getting the energy gradient, we're being |
354 |
|
|
called to test the waters at possible new locations, in which |
355 |
|
|
case we can't be changing the effective neighborhood */ |
356 |
|
|
ntrue = AIR_TRUE; |
357 |
|
|
} |
358 |
|
|
} else { |
359 |
|
|
/* never trying neighborhood caching */ |
360 |
|
|
nlist = AIR_FALSE; |
361 |
|
|
ntrue = AIR_TRUE; |
362 |
|
|
} |
363 |
|
|
} else { |
364 |
|
|
fprintf(stderr, "%s: process mode %d unrecognized!\n", me, |
365 |
|
|
task->processMode); |
366 |
|
|
return AIR_NAN; |
367 |
|
|
} |
368 |
|
|
|
369 |
|
|
/* NOTE: can't have both nlist and ntrue false. possibilities are: |
370 |
|
|
** |
371 |
|
|
** nlist: |
372 |
|
|
** true false |
373 |
|
|
** true X X |
374 |
|
|
** ntrue: |
375 |
|
|
** false X |
376 |
|
|
*/ |
377 |
|
|
/* |
378 |
|
|
printf("!%s(%u), nlist = %d, ntrue = %d\n", me, point->idtag, |
379 |
|
|
nlist, ntrue); |
380 |
|
|
*/ |
381 |
|
|
/* set nnum and task->neigh[] */ |
382 |
|
|
if (ntrue) { |
383 |
|
|
/* this finds the over-inclusive set of all possible interacting |
384 |
|
|
points, based on bin membership as well the task's add queue */ |
385 |
|
|
nnum = _neighBinPoints(task, bin, point, 1.0); |
386 |
|
|
if (nlist) { |
387 |
|
|
airArrayLenSet(point->neighPointArr, 0); |
388 |
|
|
} |
389 |
|
|
} else { |
390 |
|
|
/* (nlist true) this iter we re-use this point's existing neighbor |
391 |
|
|
list, copying it into the the task's neighbor list to simulate |
392 |
|
|
the action of _neighBinPoints() */ |
393 |
|
|
nnum = point->neighPointNum; |
394 |
|
|
for (nidx=0; nidx<nnum; nidx++) { |
395 |
|
|
task->neighPoint[nidx] = point->neighPoint[nidx]; |
396 |
|
|
} |
397 |
|
|
} |
398 |
|
|
|
399 |
|
|
/* loop through neighbor points */ |
400 |
|
|
spaDistSqMax = (task->pctx->sysParm.radiusSpace |
401 |
|
|
* task->pctx->sysParm.radiusSpace); |
402 |
|
|
/* |
403 |
|
|
printf("%s: radiusSpace = %g -> spaDistSqMax = %g\n", me, |
404 |
|
|
task->pctx->sysParm.radiusSpace, spaDistSqMax); |
405 |
|
|
*/ |
406 |
|
|
/* modeWghtSum = 0; */ |
407 |
|
|
energySum = 0; |
408 |
|
|
point->neighInterNum = 0; |
409 |
|
|
point->neighDistMean = 0.0; |
410 |
|
|
if (pullProcessModeNeighLearn == task->processMode) { |
411 |
|
|
ELL_10V_ZERO_SET(point->neighCovar); |
412 |
|
|
point->stability = 0.0; |
413 |
|
|
#if PULL_TANCOVAR |
414 |
|
|
if (task->pctx->ispec[pullInfoTangent1]) { |
415 |
|
|
double *tng; |
416 |
|
|
float outer[9]; |
417 |
|
|
tng = point->info + task->pctx->infoIdx[pullInfoTangent1]; |
418 |
|
|
ELL_3MV_OUTER_TT(outer, float, tng, tng); |
419 |
|
|
point->neighTanCovar[0] = outer[0]; |
420 |
|
|
point->neighTanCovar[1] = outer[1]; |
421 |
|
|
point->neighTanCovar[2] = outer[2]; |
422 |
|
|
point->neighTanCovar[3] = outer[4]; |
423 |
|
|
point->neighTanCovar[4] = outer[5]; |
424 |
|
|
point->neighTanCovar[5] = outer[8]; |
425 |
|
|
} |
426 |
|
|
#endif |
427 |
|
|
} |
428 |
|
|
if (egradSum) { |
429 |
|
|
ELL_4V_SET(egradSum, 0, 0, 0, 0); |
430 |
|
|
} |
431 |
|
|
for (nidx=0; nidx<nnum; nidx++) { |
432 |
|
|
double diff[4], spaDistSq, spaDist, sclDist, enr, egrad[4]; |
433 |
|
|
pullPoint *herPoint; |
434 |
|
|
|
435 |
|
|
herPoint = task->neighPoint[nidx]; |
436 |
|
|
if (herPoint->status & PULL_STATUS_NIXME_BIT) { |
437 |
|
|
/* this point is not long for this world, pass over it */ |
438 |
|
|
continue; |
439 |
|
|
} |
440 |
|
|
ELL_4V_SUB(diff, point->pos, herPoint->pos); /* me - her */ |
441 |
|
|
spaDistSq = ELL_3V_DOT(diff, diff); |
442 |
|
|
/* |
443 |
|
|
printf("!%s: %u:%g,%g,%g <-- %u:%g,%g,%g = sqd %g %s %g\n", me, |
444 |
|
|
point->idtag, point->pos[0], point->pos[1], point->pos[2], |
445 |
|
|
herPoint->idtag, |
446 |
|
|
herPoint->pos[0], herPoint->pos[1], herPoint->pos[2], |
447 |
|
|
spaDistSq, spaDistSq > spaDistSqMax ? ">" : "<=", spaDistSqMax); |
448 |
|
|
*/ |
449 |
|
|
if (spaDistSq > spaDistSqMax) { |
450 |
|
|
continue; |
451 |
|
|
} |
452 |
|
|
sclDist = AIR_ABS(diff[3]); |
453 |
|
|
if (sclDist > task->pctx->sysParm.radiusScale) { |
454 |
|
|
continue; |
455 |
|
|
} |
456 |
|
|
spaDist = sqrt(spaDistSq); |
457 |
|
|
/* we pass spaDist to avoid recomputing sqrt(), and sclDist for |
458 |
|
|
stupid consistency */ |
459 |
|
|
enr = _pullEnergyInterParticle(task->pctx, point, herPoint, |
460 |
|
|
spaDist, sclDist, |
461 |
|
|
egradSum ? egrad : NULL); |
462 |
|
|
#if 0 |
463 |
|
|
/* sanity checking on energy derivatives */ |
464 |
|
|
if (enr && egradSum) { |
465 |
|
|
double _pos[4], tdf[4], ee[2], eps=0.000001, apegrad[4], quot[4]; |
466 |
|
|
unsigned int cord, pan; |
467 |
|
|
ELL_4V_COPY(_pos, point->pos); |
468 |
|
|
|
469 |
|
|
for (cord=0; cord<=3; cord++) { |
470 |
|
|
for (pan=0; pan<=1; pan++) { |
471 |
|
|
point->pos[cord] = _pos[cord] + (!pan ? -1 : +1)*eps; |
472 |
|
|
ELL_4V_SUB(tdf, point->pos, herPoint->pos); |
473 |
|
|
ee[pan] = _pullEnergyInterParticle(task->pctx, point, herPoint, |
474 |
|
|
ELL_3V_LEN(tdf), AIR_ABS(tdf[3]), NULL); |
475 |
|
|
} |
476 |
|
|
point->pos[cord] = _pos[cord]; |
477 |
|
|
apegrad[cord] = (ee[1] - ee[0])/(2*eps); |
478 |
|
|
quot[cord] = apegrad[cord]/egrad[cord]; |
479 |
|
|
} |
480 |
|
|
if ( AIR_ABS(1.0 - quot[0]) > 0.01 || |
481 |
|
|
AIR_ABS(1.0 - quot[1]) > 0.01 || |
482 |
|
|
AIR_ABS(1.0 - quot[2]) > 0.01 || |
483 |
|
|
(task->pctx->haveScale && AIR_ABS(1.0 - quot[3]) > 0.01) ) { |
484 |
|
|
printf("!%s(%u<-%u): ---------- claim egrad (%g,%g,%g,%g)\n", me, |
485 |
|
|
point->idtag, herPoint->idtag, egrad[0], egrad[1], egrad[2], egrad[3]); |
486 |
|
|
printf("!%s(%u<-%u): measr egrad (%g,%g,%g,%g)\n", me, |
487 |
|
|
point->idtag, herPoint->idtag, apegrad[0], apegrad[1], apegrad[2], apegrad[3]); |
488 |
|
|
printf("!%s(%u<-%u): quot (%g,%g,%g,%g)\n", me, |
489 |
|
|
point->idtag, herPoint->idtag, quot[0], quot[1], quot[2], quot[3]); |
490 |
|
|
} |
491 |
|
|
|
492 |
|
|
ELL_4V_COPY(point->pos, _pos); |
493 |
|
|
} |
494 |
|
|
#endif |
495 |
|
|
|
496 |
|
|
if (enr) { |
497 |
|
|
/* there is some non-zero energy due to her; and we assume that |
498 |
|
|
its not just a fluke zero-crossing of the potential profile */ |
499 |
|
|
double ndist; |
500 |
|
|
|
501 |
|
|
point->neighInterNum++; |
502 |
|
|
if (nlist && ntrue) { |
503 |
|
|
unsigned int ii; |
504 |
|
|
/* we have to record that we had an interaction with this point */ |
505 |
|
|
ii = airArrayLenIncr(point->neighPointArr, 1); |
506 |
|
|
point->neighPoint[ii] = herPoint; |
507 |
|
|
} |
508 |
|
|
energySum += enr; |
509 |
|
|
ELL_3V_SCALE(diff, 1.0/task->pctx->sysParm.radiusSpace, diff); |
510 |
|
|
if (task->pctx->haveScale) { |
511 |
|
|
diff[3] /= task->pctx->sysParm.radiusScale; |
512 |
|
|
} |
513 |
|
|
ndist = ELL_4V_LEN(diff); |
514 |
|
|
point->neighDistMean += ndist; |
515 |
|
|
if (pullProcessModeNeighLearn == task->processMode) { |
516 |
|
|
float outer[16]; |
517 |
|
|
ELL_4MV_OUTER_TT(outer, float, diff, diff); |
518 |
|
|
point->neighCovar[0] += outer[0]; |
519 |
|
|
point->neighCovar[1] += outer[1]; |
520 |
|
|
point->neighCovar[2] += outer[2]; |
521 |
|
|
point->neighCovar[3] += outer[3]; |
522 |
|
|
point->neighCovar[4] += outer[5]; |
523 |
|
|
point->neighCovar[5] += outer[6]; |
524 |
|
|
point->neighCovar[6] += outer[7]; |
525 |
|
|
point->neighCovar[7] += outer[10]; |
526 |
|
|
point->neighCovar[8] += outer[11]; |
527 |
|
|
point->neighCovar[9] += outer[15]; |
528 |
|
|
#if PULL_TANCOVAR |
529 |
|
|
if (task->pctx->ispec[pullInfoTangent1]) { |
530 |
|
|
double *tng; |
531 |
|
|
tng = herPoint->info + task->pctx->infoIdx[pullInfoTangent1]; |
532 |
|
|
ELL_3MV_OUTER_TT(outer, float, tng, tng); |
533 |
|
|
point->neighTanCovar[0] += outer[0]; |
534 |
|
|
point->neighTanCovar[1] += outer[1]; |
535 |
|
|
point->neighTanCovar[2] += outer[2]; |
536 |
|
|
point->neighTanCovar[3] += outer[4]; |
537 |
|
|
point->neighTanCovar[4] += outer[5]; |
538 |
|
|
point->neighTanCovar[5] += outer[8]; |
539 |
|
|
} |
540 |
|
|
#endif |
541 |
|
|
} |
542 |
|
|
if (egradSum) { |
543 |
|
|
ELL_4V_INCR(egradSum, egrad); |
544 |
|
|
} |
545 |
|
|
} |
546 |
|
|
} |
547 |
|
|
|
548 |
|
|
#define CNORM(M) \ |
549 |
|
|
sqrt(M[0]*M[0] + 2*M[1]*M[1] + 2*M[2]*M[2] + 2*M[3]*M[3] \ |
550 |
|
|
+ M[4]*M[4] + 2*M[5]*M[5] + 2*M[6]*M[6] \ |
551 |
|
|
+ M[7]*M[7] + 2*M[8]*M[8] \ |
552 |
|
|
+ M[9]*M[9]) |
553 |
|
|
#define CTRACE(M) (M[0] + M[4] + M[7] + M[9]) |
554 |
|
|
|
555 |
|
|
/* finish computing things averaged over neighbors */ |
556 |
|
|
if (point->neighInterNum) { |
557 |
|
|
point->neighDistMean /= point->neighInterNum; |
558 |
|
|
if (pullProcessModeNeighLearn == task->processMode) { |
559 |
|
|
float ncs[4], ncsLen, *cov, sxx, syy, scl; |
560 |
|
|
cov = point->neighCovar; |
561 |
|
|
ELL_10V_SCALE(cov, 1.0f/point->neighInterNum, cov); |
562 |
|
|
/* Stability is related to the angle between S=(0,0,0,1) and the |
563 |
|
|
projection of S onto the tangent surface; we approximate that |
564 |
|
|
by finding the product neighCovar*S == last column of neighCovar*/ |
565 |
|
|
ELL_4V_SET(ncs, cov[3], cov[6], cov[8], cov[9]); |
566 |
|
|
ELL_4V_NORM_TT(ncs, float, ncs, ncsLen); |
567 |
|
|
if (ncsLen) { |
568 |
|
|
syy = ncs[3]; |
569 |
|
|
scl = AIR_CAST(float, (task->pctx->flag.scaleIsTau |
570 |
|
|
? gageSigOfTau(point->pos[3]) |
571 |
|
|
: point->pos[3])); |
572 |
|
|
if (scl) { |
573 |
|
|
sxx = AIR_CAST(float, ELL_3V_LEN(ncs))/scl; |
574 |
|
|
point->stability = AIR_CAST(float, atan2(syy, sxx)/(AIR_PI/2)); |
575 |
|
|
} else { |
576 |
|
|
point->stability = 0; |
577 |
|
|
} |
578 |
|
|
} else { |
579 |
|
|
/* HEY: probably bug that we can have *zero* last column in covar, |
580 |
|
|
and yet have non-zero point->neighInterNum, right? */ |
581 |
|
|
point->stability = 0; |
582 |
|
|
} |
583 |
|
|
if (!AIR_EXISTS(point->stability)) { |
584 |
|
|
fprintf(stderr, "!%s(%u): bad stability %g\n", me, |
585 |
|
|
point->idtag, point->stability); |
586 |
|
|
fprintf(stderr, "%g %g %g %g\n", cov[3], cov[6], cov[8], cov[9]); |
587 |
|
|
fprintf(stderr, "%g %g %g %g\n", ncs[0], ncs[1], ncs[2], ncs[3]); |
588 |
|
|
fprintf(stderr, "sxx %g syy %g\n", sxx, syy); |
589 |
|
|
} |
590 |
|
|
#if PULL_TANCOVAR |
591 |
|
|
/* using 1 + # neigh because this includes tan1 of point itself */ |
592 |
|
|
ELL_6V_SCALE(point->neighTanCovar, 1.0f/(1 + point->neighInterNum), |
593 |
|
|
point->neighTanCovar); |
594 |
|
|
#endif |
595 |
|
|
} |
596 |
|
|
} else { |
597 |
|
|
/* we had no neighbors at all */ |
598 |
|
|
point->neighDistMean = 0.0; /* shouldn't happen in any normal case */ |
599 |
|
|
/* point->neighCovar,neighTanCovar stay as initialized above */ |
600 |
|
|
} |
601 |
|
|
return energySum; |
602 |
|
|
} |
603 |
|
|
|
604 |
|
|
static double |
605 |
|
|
_energyFromImage(pullTask *task, pullPoint *point, |
606 |
|
|
/* output */ |
607 |
|
|
double egradSum[4]) { |
608 |
|
|
static const char me[]="_energyFromImage"; |
609 |
|
|
double energy, grad3[3], _gamma; |
610 |
|
|
int probed; |
611 |
|
|
|
612 |
|
|
/* not sure I have the logic for this right |
613 |
|
|
int ptrue; |
614 |
|
|
if (task->pctx->probeProb < 1 && allowProbeProb) { |
615 |
|
|
if (egrad) { |
616 |
|
|
ptrue = (0 == task->pctx->iter |
617 |
|
|
|| airDrandMT_r(task->rng) < task->pctx->probeProb); |
618 |
|
|
} else { |
619 |
|
|
ptrue = AIR_FALSE; |
620 |
|
|
} |
621 |
|
|
} else { |
622 |
|
|
ptrue = AIR_TRUE; |
623 |
|
|
} |
624 |
|
|
*/ |
625 |
|
|
probed = AIR_FALSE; |
626 |
|
|
|
627 |
|
|
/* a better name for this would be |
628 |
|
|
"probe only if you haven't already probed" */ |
629 |
|
|
#define MAYBEPROBE \ |
630 |
|
|
if (!probed) { \ |
631 |
|
|
if (pullProbe(task, point)) { \ |
632 |
|
|
fprintf(stderr, "%s: problem probing!!!\n%s\n", me, biffGetDone(PULL)); \ |
633 |
|
|
} \ |
634 |
|
|
probed = AIR_TRUE; \ |
635 |
|
|
} |
636 |
|
|
|
637 |
|
|
_gamma = task->pctx->sysParm.gamma; |
638 |
|
|
energy = 0; |
639 |
|
|
if (egradSum) { |
640 |
|
|
ELL_4V_SET(egradSum, 0, 0, 0, 0); |
641 |
|
|
} |
642 |
|
|
if (task->pctx->flag.energyFromStrength |
643 |
|
|
&& task->pctx->ispec[pullInfoStrength]) { |
644 |
|
|
double deltaScale, str0, str1, scl0, scl1, enr; |
645 |
|
|
int sign; |
646 |
|
|
if (!egradSum) { |
647 |
|
|
/* just need the strength */ |
648 |
|
|
MAYBEPROBE; |
649 |
|
|
enr = pullPointScalar(task->pctx, point, pullInfoStrength, |
650 |
|
|
NULL, NULL); |
651 |
|
|
energy += -_gamma*enr; |
652 |
|
|
} else { |
653 |
|
|
/* need strength and its gradient */ |
654 |
|
|
/* randomize choice between forward and backward difference */ |
655 |
|
|
/* NOTE: since you only need one bit of random, you could re-used |
656 |
|
|
a random int and look through its bits to determine forw vs |
657 |
|
|
back differences, but this is probably not the bottleneck */ |
658 |
|
|
sign = 2*AIR_CAST(int, airRandInt_r(task->rng, 2)) - 1; |
659 |
|
|
deltaScale = task->pctx->bboxMax[3] - task->pctx->bboxMin[3]; |
660 |
|
|
deltaScale *= sign*_PULL_STRENGTH_ENERGY_DELTA_SCALE; |
661 |
|
|
scl1 = (point->pos[3] += deltaScale); |
662 |
|
|
pullProbe(task, point); /* *not* MAYBEPROBE */ |
663 |
|
|
str1 = pullPointScalar(task->pctx, point, pullInfoStrength, |
664 |
|
|
NULL, NULL); |
665 |
|
|
scl0 = (point->pos[3] -= deltaScale); |
666 |
|
|
MAYBEPROBE; |
667 |
|
|
str0 = pullPointScalar(task->pctx, point, pullInfoStrength, |
668 |
|
|
NULL, NULL); |
669 |
|
|
energy += -_gamma*str0; |
670 |
|
|
egradSum[3] += -_gamma*(str1 - str0)/(scl1 - scl0); |
671 |
|
|
/* |
672 |
|
|
if (1560 < task->pctx->iter && 2350 == point->idtag) { |
673 |
|
|
printf("%s(%u): egrad[3] = %g*((%g-%g)/(%g-%g) = %g/%g = %g)" |
674 |
|
|
" = %g -> %g\n", |
675 |
|
|
me, point->idtag, task->pctx->sysParm.gamma, |
676 |
|
|
str1, str0, scl1, scl0, |
677 |
|
|
str1 - str0, scl1 - scl0, |
678 |
|
|
(str1 - str0)/(scl1 - scl0), |
679 |
|
|
task->pctx->sysParm.gamma*(str1 - str0)/(scl1 - scl0), |
680 |
|
|
egradSum[3]); |
681 |
|
|
} |
682 |
|
|
*/ |
683 |
|
|
} |
684 |
|
|
} |
685 |
|
|
/* Note that height doesn't contribute to the energy if there is |
686 |
|
|
a constraint associated with it */ |
687 |
|
|
if (task->pctx->ispec[pullInfoHeight] |
688 |
|
|
&& !task->pctx->ispec[pullInfoHeight]->constraint |
689 |
|
|
&& !(task->pctx->ispec[pullInfoHeightLaplacian] |
690 |
|
|
&& task->pctx->ispec[pullInfoHeightLaplacian]->constraint)) { |
691 |
|
|
MAYBEPROBE; |
692 |
|
|
energy += pullPointScalar(task->pctx, point, pullInfoHeight, |
693 |
|
|
grad3, NULL); |
694 |
|
|
if (egradSum) { |
695 |
|
|
ELL_3V_INCR(egradSum, grad3); |
696 |
|
|
} |
697 |
|
|
} |
698 |
|
|
if (task->pctx->ispec[pullInfoIsovalue] |
699 |
|
|
&& !task->pctx->ispec[pullInfoIsovalue]->constraint) { |
700 |
|
|
/* we're only going towards an isosurface, not constrained to it |
701 |
|
|
==> set up a parabolic potential well around the isosurface */ |
702 |
|
|
double val; |
703 |
|
|
MAYBEPROBE; |
704 |
|
|
val = pullPointScalar(task->pctx, point, pullInfoIsovalue, |
705 |
|
|
grad3, NULL); |
706 |
|
|
energy += val*val; |
707 |
|
|
if (egradSum) { |
708 |
|
|
ELL_3V_SCALE_INCR(egradSum, 2*val, grad3); |
709 |
|
|
} |
710 |
|
|
} |
711 |
|
|
return energy; |
712 |
|
|
} |
713 |
|
|
#undef MAYBEPROBE |
714 |
|
|
|
715 |
|
|
/* |
716 |
|
|
** NOTE that the "egrad" being non-NULL has consequences for what gets |
717 |
|
|
** computed in _energyFromImage and _pullEnergyFromPoints: |
718 |
|
|
** |
719 |
|
|
** NULL "egrad": we're simply learning the energy (and want to know it |
720 |
|
|
** as truthfully as possible) for the sake of inspecting system state |
721 |
|
|
** |
722 |
|
|
** non-NULL "egrad": we're learning the current energy, but the real point |
723 |
|
|
** is to determine how to move the point to lower energy |
724 |
|
|
** |
725 |
|
|
** the ignoreImage flag is a hack, to allow _pullPointProcessAdding to |
726 |
|
|
** do descent on a new point according to other points, but not the |
727 |
|
|
** image. |
728 |
|
|
*/ |
729 |
|
|
double |
730 |
|
|
_pullPointEnergyTotal(pullTask *task, pullBin *bin, pullPoint *point, |
731 |
|
|
int ignoreImage, |
732 |
|
|
/* output */ |
733 |
|
|
double egrad[4]) { |
734 |
|
|
static const char me[]="_pullPointEnergyTotal"; |
735 |
|
|
double enrIm, enrPt, egradIm[4], egradPt[4], energy; |
736 |
|
|
|
737 |
|
|
ELL_4V_SET(egradIm, 0, 0, 0, 0); |
738 |
|
|
ELL_4V_SET(egradPt, 0, 0, 0, 0); |
739 |
|
|
if (!( ignoreImage || 1.0 == task->pctx->sysParm.alpha )) { |
740 |
|
|
enrIm = _energyFromImage(task, point, egrad ? egradIm : NULL); |
741 |
|
|
task->pctx->count[pullCountEnergyFromImage] += 1; |
742 |
|
|
if (egrad) { |
743 |
|
|
task->pctx->count[pullCountForceFromImage] += 1; |
744 |
|
|
} |
745 |
|
|
} else { |
746 |
|
|
enrIm = 0; |
747 |
|
|
} |
748 |
|
|
if (task->pctx->sysParm.alpha > 0.0) { |
749 |
|
|
enrPt = _pullEnergyFromPoints(task, bin, point, egrad ? egradPt : NULL); |
750 |
|
|
task->pctx->count[pullCountEnergyFromPoints] += 1; |
751 |
|
|
if (egrad) { |
752 |
|
|
task->pctx->count[pullCountForceFromPoints] += 1; |
753 |
|
|
} |
754 |
|
|
} else { |
755 |
|
|
enrPt = 0; |
756 |
|
|
} |
757 |
|
|
energy = AIR_LERP(task->pctx->sysParm.alpha, enrIm, enrPt); |
758 |
|
|
/* |
759 |
|
|
printf("!%s(%u): energy = lerp(%g, im %g, pt %g) = %g\n", me, |
760 |
|
|
point->idtag, task->pctx->sysParm.alpha, enrIm, enrPt, energy); |
761 |
|
|
*/ |
762 |
|
|
if (egrad) { |
763 |
|
|
ELL_4V_LERP(egrad, task->pctx->sysParm.alpha, egradIm, egradPt); |
764 |
|
|
/* |
765 |
|
|
printf("!%s(%u): egradIm = %g %g %g %g\n", me, point->idtag, |
766 |
|
|
egradIm[0], egradIm[1], egradIm[2], egradIm[3]); |
767 |
|
|
printf("!%s(%u): egradPt = %g %g %g %g\n", me, point->idtag, |
768 |
|
|
egradPt[0], egradPt[1], egradPt[2], egradPt[3]); |
769 |
|
|
printf("!%s(%u): ---> force = %g %g %g %g\n", me, |
770 |
|
|
point->idtag, force[0], force[1], force[2], force[3]); |
771 |
|
|
*/ |
772 |
|
|
} |
773 |
|
|
if (task->pctx->sysParm.wall) { |
774 |
|
|
unsigned int axi; |
775 |
|
|
double dwe; /* derivative of wall energy */ |
776 |
|
|
for (axi=0; axi<4; axi++) { |
777 |
|
|
dwe = point->pos[axi] - task->pctx->bboxMin[axi]; |
778 |
|
|
if (dwe > 0) { |
779 |
|
|
/* pos not below min */ |
780 |
|
|
dwe = point->pos[axi] - task->pctx->bboxMax[axi]; |
781 |
|
|
if (dwe < 0) { |
782 |
|
|
/* pos not above max */ |
783 |
|
|
dwe = 0; |
784 |
|
|
} |
785 |
|
|
} |
786 |
|
|
energy += task->pctx->sysParm.wall*dwe*dwe/2; |
787 |
|
|
if (egrad) { |
788 |
|
|
egrad[axi] += task->pctx->sysParm.wall*dwe; |
789 |
|
|
} |
790 |
|
|
} |
791 |
|
|
} |
792 |
|
|
if (!AIR_EXISTS(energy)) { |
793 |
|
|
fprintf(stderr, "!%s(%u): oops- non-exist energy %g\n", me, point->idtag, |
794 |
|
|
energy); |
795 |
|
|
} |
796 |
|
|
return energy; |
797 |
|
|
} |
798 |
|
|
|
799 |
|
|
/* |
800 |
|
|
** distance limit on a particles motion in both r and s, |
801 |
|
|
** in rs-normalized space (sqrt((r/radiusSpace)^2 + (s/radiusScale)^2)) |
802 |
|
|
** |
803 |
|
|
** This means that if particles are jammed together in space, |
804 |
|
|
** they aren't allowed to move very far in scale, either, which |
805 |
|
|
** is a little weird, but probably okay. |
806 |
|
|
*/ |
807 |
|
|
double |
808 |
|
|
_pullDistLimit(pullTask *task, pullPoint *point) { |
809 |
|
|
double ret; |
810 |
|
|
|
811 |
|
|
if (point->neighDistMean == 0 /* no known neighbors from last iter */ |
812 |
|
|
|| pullEnergyZero == task->pctx->energySpecR->energy) { |
813 |
|
|
ret = 1; |
814 |
|
|
} else { |
815 |
|
|
ret = _PULL_DIST_CAP_RSNORM*point->neighDistMean; |
816 |
|
|
} |
817 |
|
|
/* HEY: maybe task->pctx->voxelSizeSpace or voxelSizeScale should |
818 |
|
|
be considered here? */ |
819 |
|
|
return ret; |
820 |
|
|
} |
821 |
|
|
|
822 |
|
|
/* |
823 |
|
|
** here is where the energy gradient is converted into force |
824 |
|
|
*/ |
825 |
|
|
int |
826 |
|
|
_pullPointProcessDescent(pullTask *task, pullBin *bin, pullPoint *point, |
827 |
|
|
int ignoreImage) { |
828 |
|
|
static const char me[]="_pullPointProcessDescent"; |
829 |
|
|
double energyOld, energyNew, egrad[4], force[4], posOld[4]; |
830 |
|
|
int stepBad, giveUp, hailMary; |
831 |
|
|
|
832 |
|
|
task->pctx->count[pullCountDescent] += 1; |
833 |
|
|
if (!point->stepEnergy) { |
834 |
|
|
fprintf(stderr, "\n\n\n%s: whoa, point %u step is zero!!\n\n\n\n", |
835 |
|
|
me, point->idtag); |
836 |
|
|
/* HEY: need to track down how this can originate! */ |
837 |
|
|
/* |
838 |
|
|
biffAddf(PULL, "%s: point %u step is zero!", me, point->idtag); |
839 |
|
|
return 1; |
840 |
|
|
*/ |
841 |
|
|
point->stepEnergy = task->pctx->sysParm.stepInitial/100; |
842 |
|
|
} |
843 |
|
|
|
844 |
|
|
/* learn the energy at old location, and the energy gradient */ |
845 |
|
|
energyOld = _pullPointEnergyTotal(task, bin, point, ignoreImage, egrad); |
846 |
|
|
ELL_4V_SCALE(force, -1, egrad); |
847 |
|
|
if (!( AIR_EXISTS(energyOld) && ELL_4V_EXISTS(force) )) { |
848 |
|
|
biffAddf(PULL, "%s: point %u non-exist energy or force", me, point->idtag); |
849 |
|
|
return 1; |
850 |
|
|
} |
851 |
|
|
/* |
852 |
|
|
if (1560 < task->pctx->iter && 2350 == point->idtag) { |
853 |
|
|
printf("!%s(%u): old pos = %g %g %g %g\n", me, point->idtag, |
854 |
|
|
point->pos[0], point->pos[1], |
855 |
|
|
point->pos[2], point->pos[3]); |
856 |
|
|
printf("!%s(%u): energyOld = %g; force = %g %g %g %g\n", me, |
857 |
|
|
point->idtag, energyOld, force[0], force[1], force[2], force[3]); |
858 |
|
|
} |
859 |
|
|
*/ |
860 |
|
|
if (!ELL_4V_DOT(force, force)) { |
861 |
|
|
/* this particle has no reason to go anywhere; BUT we still have to |
862 |
|
|
enforce constraint if we have one */ |
863 |
|
|
int constrFail = 0; |
864 |
|
|
__IF_DEBUG { |
865 |
|
|
fprintf(stderr, "!%s: point %u unforced ...\n", me, point->idtag); |
866 |
|
|
} |
867 |
|
|
if (task->pctx->constraint) { |
868 |
|
|
if (_pullConstraintSatisfy(task, point, |
869 |
|
|
100.0*_PULL_CONSTRAINT_TRAVEL_MAX, |
870 |
|
|
&constrFail)) { |
871 |
|
|
biffAddf(PULL, "%s: trouble", me); |
872 |
|
|
return 1; |
873 |
|
|
} |
874 |
|
|
} |
875 |
|
|
if (constrFail) { |
876 |
|
|
/* |
877 |
|
|
biffAddf(PULL, "%s: couldn't satisfy constraint on unforced %u: %s", |
878 |
|
|
me, point->idtag, airEnumStr(pullConstraintFail, constrFail)); |
879 |
|
|
return 1; |
880 |
|
|
*/ |
881 |
|
|
fprintf(stderr, "%s: *** constr sat fail on unfrced %u: " |
882 |
|
|
"%s (si# %u;%u)\n", |
883 |
|
|
me, point->idtag, airEnumStr(pullConstraintFail, constrFail), |
884 |
|
|
point->stuckIterNum, task->pctx->iterParm.stuckMax); |
885 |
|
|
point->status |= PULL_STATUS_STUCK_BIT; |
886 |
|
|
point->stuckIterNum += 1; |
887 |
|
|
if (task->pctx->iterParm.stuckMax |
888 |
|
|
&& point->stuckIterNum > task->pctx->iterParm.stuckMax) { |
889 |
|
|
point->status |= PULL_STATUS_NIXME_BIT; |
890 |
|
|
} |
891 |
|
|
} |
892 |
|
|
point->energy = energyOld; |
893 |
|
|
return 0; |
894 |
|
|
} |
895 |
|
|
|
896 |
|
|
if (task->pctx->constraint |
897 |
|
|
&& (task->pctx->ispec[pullInfoTangent1] |
898 |
|
|
|| task->pctx->ispec[pullInfoNegativeTangent1])) { |
899 |
|
|
/* we have a constraint, so do something to get the force more |
900 |
|
|
tangential to the constraint manifold (only in the spatial axes) */ |
901 |
|
|
double proj[9], pfrc[3]; |
902 |
|
|
_pullConstraintTangent(task, point, proj); |
903 |
|
|
ELL_3MV_MUL(pfrc, proj, force); |
904 |
|
|
ELL_3V_COPY(force, pfrc); |
905 |
|
|
/* force[3] untouched */ |
906 |
|
|
} |
907 |
|
|
/* |
908 |
|
|
if (1560 < task->pctx->iter && 2350 == point->idtag) { |
909 |
|
|
printf("!%s(%u): post-constraint tan: force = %g %g %g %g\n", me, |
910 |
|
|
point->idtag, force[0], force[1], force[2], force[3]); |
911 |
|
|
printf(" precap stepEnergy = %g\n", point->stepEnergy); |
912 |
|
|
} |
913 |
|
|
*/ |
914 |
|
|
/* Cap particle motion. The point is only allowed to move at most unit |
915 |
|
|
distance in rs-normalized space, which may mean that motion in r |
916 |
|
|
or s is effectively cramped by crowding in the other axis, oh well. |
917 |
|
|
Also, particle motion is limited in terms of spatial voxel size, |
918 |
|
|
and (if haveScale) the average distance between scale samples */ |
919 |
|
|
if (1) { |
920 |
|
|
double capvec[4], spcLen, sclLen, max, distLimit; |
921 |
|
|
|
922 |
|
|
/* limits based on distLimit in rs-normalized space */ |
923 |
|
|
distLimit = _pullDistLimit(task, point); |
924 |
|
|
ELL_4V_SCALE(capvec, point->stepEnergy, force); |
925 |
|
|
spcLen = ELL_3V_LEN(capvec)/task->pctx->sysParm.radiusSpace; |
926 |
|
|
sclLen = AIR_ABS(capvec[3])/task->pctx->sysParm.radiusScale; |
927 |
|
|
max = AIR_MAX(spcLen, sclLen); |
928 |
|
|
if (max > distLimit) { |
929 |
|
|
point->stepEnergy *= distLimit/max; |
930 |
|
|
} |
931 |
|
|
/* limits based on voxelSizeSpace and voxelSizeScale */ |
932 |
|
|
ELL_4V_SCALE(capvec, point->stepEnergy, force); |
933 |
|
|
spcLen = ELL_3V_LEN(capvec)/task->pctx->voxelSizeSpace; |
934 |
|
|
if (task->pctx->haveScale) { |
935 |
|
|
sclLen = AIR_ABS(capvec[3])/task->pctx->voxelSizeScale; |
936 |
|
|
max = AIR_MAX(spcLen, sclLen); |
937 |
|
|
} else { |
938 |
|
|
max = spcLen; |
939 |
|
|
} |
940 |
|
|
if (max > _PULL_DIST_CAP_VOXEL) { |
941 |
|
|
point->stepEnergy *= _PULL_DIST_CAP_VOXEL/max; |
942 |
|
|
} |
943 |
|
|
} |
944 |
|
|
/* |
945 |
|
|
if (1560 < task->pctx->iter && 2350 == point->idtag) { |
946 |
|
|
printf(" postcap stepEnergy = %g\n", point->stepEnergy); |
947 |
|
|
} |
948 |
|
|
*/ |
949 |
|
|
/* turn off stuck bit, will turn it on again if needed */ |
950 |
|
|
point->status &= ~PULL_STATUS_STUCK_BIT; |
951 |
|
|
ELL_4V_COPY(posOld, point->pos); |
952 |
|
|
_pullPointHistInit(point); |
953 |
|
|
_pullPointHistAdd(point, pullCondOld, AIR_NAN); |
954 |
|
|
/* try steps along force until we succcessfully lower energy */ |
955 |
|
|
hailMary = AIR_FALSE; |
956 |
|
|
do { |
957 |
|
|
int constrFail, energyIncr; |
958 |
|
|
giveUp = AIR_FALSE; |
959 |
|
|
ELL_4V_SCALE_ADD2(point->pos, 1.0, posOld, |
960 |
|
|
point->stepEnergy, force); |
961 |
|
|
/* |
962 |
|
|
if (1560 < task->pctx->iter && 2350 == point->idtag) { |
963 |
|
|
printf("!%s(%u): (iter %u) try pos = %g %g %g %g%s\n", |
964 |
|
|
me, point->idtag, task->pctx->iter, |
965 |
|
|
point->pos[0], point->pos[1], |
966 |
|
|
point->pos[2], point->pos[3], |
967 |
|
|
hailMary ? " (Hail Mary)" : ""); |
968 |
|
|
} |
969 |
|
|
*/ |
970 |
|
|
if (task->pctx->haveScale) { |
971 |
|
|
point->pos[3] = AIR_CLAMP(task->pctx->bboxMin[3], |
972 |
|
|
point->pos[3], |
973 |
|
|
task->pctx->bboxMax[3]); |
974 |
|
|
} |
975 |
|
|
task->pctx->count[pullCountTestStep] += 1; |
976 |
|
|
_pullPointHistAdd(point, pullCondEnergyTry, AIR_NAN); |
977 |
|
|
if (task->pctx->constraint) { |
978 |
|
|
if (_pullConstraintSatisfy(task, point, |
979 |
|
|
_PULL_CONSTRAINT_TRAVEL_MAX, |
980 |
|
|
&constrFail)) { |
981 |
|
|
biffAddf(PULL, "%s: trouble", me); |
982 |
|
|
return 1; |
983 |
|
|
} |
984 |
|
|
} else { |
985 |
|
|
constrFail = AIR_FALSE; |
986 |
|
|
} |
987 |
|
|
/* |
988 |
|
|
if (1560 < task->pctx->iter && 2350 == point->idtag) { |
989 |
|
|
printf("!%s(%u): post constr = %g %g %g %g (%d)\n", me, |
990 |
|
|
point->idtag, |
991 |
|
|
point->pos[0], point->pos[1], |
992 |
|
|
point->pos[2], point->pos[3], constrFail); |
993 |
|
|
} |
994 |
|
|
*/ |
995 |
|
|
if (constrFail) { |
996 |
|
|
energyNew = AIR_NAN; |
997 |
|
|
} else { |
998 |
|
|
energyNew = _pullPointEnergyTotal(task, bin, point, ignoreImage, NULL); |
999 |
|
|
} |
1000 |
|
|
energyIncr = energyNew > (energyOld |
1001 |
|
|
+ task->pctx->sysParm.energyIncreasePermit); |
1002 |
|
|
/* |
1003 |
|
|
if (1560 < task->pctx->iter && 2350 == point->idtag) { |
1004 |
|
|
printf("!%s(%u): constrFail %d; enr Old New = %g %g -> enrIncr %d\n", |
1005 |
|
|
me, point->idtag, constrFail, energyOld, energyNew, energyIncr); |
1006 |
|
|
} |
1007 |
|
|
*/ |
1008 |
|
|
stepBad = (constrFail || energyIncr); |
1009 |
|
|
if (stepBad) { |
1010 |
|
|
point->stepEnergy *= task->pctx->sysParm.backStepScale; |
1011 |
|
|
if (constrFail) { |
1012 |
|
|
_pullPointHistAdd(point, pullCondConstraintFail, AIR_NAN); |
1013 |
|
|
} else { |
1014 |
|
|
_pullPointHistAdd(point, pullCondEnergyBad, AIR_NAN); |
1015 |
|
|
} |
1016 |
|
|
/* you have a problem if you had a non-trivial force, but you can't |
1017 |
|
|
ever seem to take a small enough step to reduce energy */ |
1018 |
|
|
if (point->stepEnergy < 0.00000001) { |
1019 |
|
|
/* this can happen if the force is due to a derivative of |
1020 |
|
|
feature strength with respect to scale, which is measured |
1021 |
|
|
WITHOUT enforcing the constraint, while particle updates |
1022 |
|
|
are done WITH the constraint, in which case the computed |
1023 |
|
|
force can be completely misleading. Thus, as a last-ditch |
1024 |
|
|
effort, we try moving in the opposite direction (against |
1025 |
|
|
the force) to see if that helps */ |
1026 |
|
|
if (task->pctx->verbose > 1) { |
1027 |
|
|
printf("%s: %u %s (%u); (%g,%g,%g,%g) stepEnr %g\n", me, |
1028 |
|
|
point->idtag, hailMary ? "STUCK!" : "stuck?", |
1029 |
|
|
point->stuckIterNum, |
1030 |
|
|
point->pos[0], point->pos[1], point->pos[2], point->pos[3], |
1031 |
|
|
point->stepEnergy); |
1032 |
|
|
} |
1033 |
|
|
if (!hailMary) { |
1034 |
|
|
ELL_4V_SCALE(force, -1, force); |
1035 |
|
|
/* |
1036 |
|
|
if (4819 == point->idtag || 4828 == point->idtag) { |
1037 |
|
|
printf("!%s(%u): force now %g %g %g %g\n", me, point->idtag, |
1038 |
|
|
force[0], force[1], force[2], force[3]); |
1039 |
|
|
} |
1040 |
|
|
*/ |
1041 |
|
|
hailMary = AIR_TRUE; |
1042 |
|
|
} else { |
1043 |
|
|
/* The hail Mary pass missed too; something is really odd. |
1044 |
|
|
This can happen when the previous iteration did a sloppy job |
1045 |
|
|
enforcing the constraint, so before we move on, we enforce |
1046 |
|
|
it, twice for good measure, so that things may be better next |
1047 |
|
|
time around */ |
1048 |
|
|
if (task->pctx->constraint) { |
1049 |
|
|
if (_pullConstraintSatisfy(task, point, |
1050 |
|
|
_PULL_CONSTRAINT_TRAVEL_MAX, |
1051 |
|
|
&constrFail) |
1052 |
|
|
|| _pullConstraintSatisfy(task, point, |
1053 |
|
|
_PULL_CONSTRAINT_TRAVEL_MAX, |
1054 |
|
|
&constrFail)) { |
1055 |
|
|
biffAddf(PULL, "%s: trouble", me); |
1056 |
|
|
return 1; |
1057 |
|
|
} |
1058 |
|
|
} |
1059 |
|
|
energyNew = _pullPointEnergyTotal(task, bin, point, |
1060 |
|
|
ignoreImage, NULL); |
1061 |
|
|
point->stepEnergy = task->pctx->sysParm.stepInitial; |
1062 |
|
|
point->status |= PULL_STATUS_STUCK_BIT; |
1063 |
|
|
point->stuckIterNum += 1; |
1064 |
|
|
giveUp = AIR_TRUE; |
1065 |
|
|
} |
1066 |
|
|
} |
1067 |
|
|
} |
1068 |
|
|
} while (stepBad && !giveUp); |
1069 |
|
|
/* Hail Mary worked if (hailMary && !stepBad). It does sometimes work. */ |
1070 |
|
|
|
1071 |
|
|
/* now: unless we gave up, energy decreased, and, |
1072 |
|
|
if we have one, constraint has been met */ |
1073 |
|
|
/* |
1074 |
|
|
if (1560 < task->pctx->iter && 2350 == point->idtag) { |
1075 |
|
|
printf("!%s(%u):iter %u changed (%g,%g,%g,%g)->(%g,%g,%g,%g)\n", |
1076 |
|
|
me, point->idtag, task->pctx->iter, |
1077 |
|
|
posOld[0], posOld[1], posOld[2], posOld[3], |
1078 |
|
|
point->pos[0], point->pos[1], point->pos[2], point->pos[3]); |
1079 |
|
|
} |
1080 |
|
|
*/ |
1081 |
|
|
_pullPointHistAdd(point, pullCondNew, AIR_NAN); |
1082 |
|
|
ELL_4V_COPY(point->force, force); |
1083 |
|
|
|
1084 |
|
|
/* not recorded for the sake of this function, but for system accounting */ |
1085 |
|
|
point->energy = energyNew; |
1086 |
|
|
if (!AIR_EXISTS(energyNew)) { |
1087 |
|
|
biffAddf(PULL, "%s: point %u has non-exist final energy %g\n", |
1088 |
|
|
me, point->idtag, energyNew); |
1089 |
|
|
return 1; |
1090 |
|
|
} |
1091 |
|
|
|
1092 |
|
|
/* if its not stuck, reset stuckIterNum */ |
1093 |
|
|
if (!(point->status & PULL_STATUS_STUCK_BIT)) { |
1094 |
|
|
point->stuckIterNum = 0; |
1095 |
|
|
} else if (task->pctx->iterParm.stuckMax |
1096 |
|
|
&& point->stuckIterNum > task->pctx->iterParm.stuckMax) { |
1097 |
|
|
/* else if it is stuck then its up to us to set NIXME |
1098 |
|
|
based on point->stuckIterNum */ |
1099 |
|
|
point->status |= PULL_STATUS_NIXME_BIT; |
1100 |
|
|
} |
1101 |
|
|
|
1102 |
|
|
return 0; |
1103 |
|
|
} |
1104 |
|
|
|
1105 |
|
|
int |
1106 |
|
|
_pullPointProcess(pullTask *task, pullBin *bin, pullPoint *point) { |
1107 |
|
|
static const char me[]="_pullPointProcess"; |
1108 |
|
|
int E; |
1109 |
|
|
|
1110 |
|
|
/* |
1111 |
|
|
fprintf(stderr, "!%s(%u,%u) mode %s\n", me, point->idtag, task->pctx->iter, |
1112 |
|
|
airEnumStr(pullProcessMode, task->processMode)); |
1113 |
|
|
*/ |
1114 |
|
|
E = 0; |
1115 |
|
|
switch (task->processMode) { |
1116 |
|
|
case pullProcessModeDescent: |
1117 |
|
|
E = _pullPointProcessDescent(task, bin, point, |
1118 |
|
|
AIR_FALSE |
1119 |
|
|
/* !task->pctx->haveScale ignoreImage */); |
1120 |
|
|
break; |
1121 |
|
|
case pullProcessModeNeighLearn: |
1122 |
|
|
E = _pullPointProcessNeighLearn(task, bin, point); |
1123 |
|
|
break; |
1124 |
|
|
case pullProcessModeAdding: |
1125 |
|
|
if (!task->pctx->flag.noAdd) { |
1126 |
|
|
E = _pullPointProcessAdding(task, bin, point); |
1127 |
|
|
} |
1128 |
|
|
break; |
1129 |
|
|
case pullProcessModeNixing: |
1130 |
|
|
E = _pullPointProcessNixing(task, bin, point); |
1131 |
|
|
break; |
1132 |
|
|
default: |
1133 |
|
|
biffAddf(PULL, "%s: process mode %d unrecognized", me, task->processMode); |
1134 |
|
|
return 1; |
1135 |
|
|
break; |
1136 |
|
|
} |
1137 |
|
|
if (E) { |
1138 |
|
|
biffAddf(PULL, "%s: trouble", me); |
1139 |
|
|
return 1; |
1140 |
|
|
} |
1141 |
|
|
if (task->pctx->flag.zeroZ) { |
1142 |
|
|
point->pos[2] = 0; |
1143 |
|
|
} |
1144 |
|
|
return 0; |
1145 |
|
|
} |
1146 |
|
|
|
1147 |
|
|
int |
1148 |
|
|
pullBinProcess(pullTask *task, unsigned int myBinIdx) { |
1149 |
|
|
static const char me[]="pullBinProcess"; |
1150 |
|
|
pullBin *myBin; |
1151 |
|
|
unsigned int myPointIdx; |
1152 |
|
|
|
1153 |
|
|
if (task->pctx->verbose > 2) { |
1154 |
|
|
printf("%s(%s): doing bin %u\n", me, |
1155 |
|
|
airEnumStr(pullProcessMode, task->processMode), myBinIdx); |
1156 |
|
|
} |
1157 |
|
|
myBin = task->pctx->bin + myBinIdx; |
1158 |
|
|
for (myPointIdx=0; myPointIdx<myBin->pointNum; myPointIdx++) { |
1159 |
|
|
pullPoint *point; |
1160 |
|
|
if (task->pctx->verbose > 1 |
1161 |
|
|
&& task->pctx->pointNum > _PULL_PROGRESS_POINT_NUM_MIN |
1162 |
|
|
&& !task->pctx->flag.binSingle |
1163 |
|
|
&& task->pctx->progressBinMod |
1164 |
|
|
&& 0 == myBinIdx % task->pctx->progressBinMod) { |
1165 |
|
|
printf("."); fflush(stdout); |
1166 |
|
|
} |
1167 |
|
|
point = myBin->point[myPointIdx]; |
1168 |
|
|
if (task->pctx->verbose > 2) { |
1169 |
|
|
printf("%s(%s) processing (bin %u)->point[%u] %u\n", me, |
1170 |
|
|
airEnumStr(pullProcessMode, task->processMode), |
1171 |
|
|
myBinIdx, myPointIdx, point->idtag); |
1172 |
|
|
} |
1173 |
|
|
if (_pullPointProcess(task, myBin, point)) { |
1174 |
|
|
biffAddf(PULL, "%s: on point %u of bin %u\n", me, |
1175 |
|
|
myPointIdx, myBinIdx); |
1176 |
|
|
return 1; |
1177 |
|
|
} |
1178 |
|
|
task->stuckNum += (point->status & PULL_STATUS_STUCK_BIT); |
1179 |
|
|
} /* for myPointIdx */ |
1180 |
|
|
|
1181 |
|
|
return 0; |
1182 |
|
|
} |
1183 |
|
|
|
1184 |
|
|
int |
1185 |
|
|
pullGammaLearn(pullContext *pctx) { |
1186 |
|
|
static const char me[]="pullGammaLearn"; |
1187 |
|
|
unsigned int binIdx, pointIdx, pointNum; |
1188 |
|
|
pullBin *bin; |
1189 |
|
|
pullPoint *point; |
1190 |
|
|
pullTask *task; |
1191 |
|
|
double deltaScale, scl, beta, wellX=0, wellY=0, |
1192 |
|
|
*strdd, *gmag, meanGmag, meanStrdd, wght, wghtSum; |
1193 |
|
|
airArray *mop; |
1194 |
|
|
|
1195 |
|
|
if (!pctx) { |
1196 |
|
|
biffAddf(PULL, "%s: got NULL pointer", me); |
1197 |
|
|
return 1; |
1198 |
|
|
} |
1199 |
|
|
if (!pctx->haveScale) { |
1200 |
|
|
biffAddf(PULL, "%s: not using scale-space", me); |
1201 |
|
|
return 1; |
1202 |
|
|
} |
1203 |
|
|
if (pullInterTypeAdditive == pctx->interType) { |
1204 |
|
|
if (pullEnergyButterworthParabola != pctx->energySpecS->energy) { |
1205 |
|
|
biffAddf(PULL, "%s: want %s energy along scale, not %s", me, |
1206 |
|
|
pullEnergyButterworthParabola->name, |
1207 |
|
|
pctx->energySpecS->energy->name); |
1208 |
|
|
return 1; |
1209 |
|
|
} |
1210 |
|
|
} else if (pullInterTypeSeparable == pctx->interType) { |
1211 |
|
|
wellY = pctx->energySpecR->energy->well(&wellX, |
1212 |
|
|
pctx->energySpecR->parm); |
1213 |
|
|
if (!( wellY < 0 )) { |
1214 |
|
|
biffAddf(PULL, "%s: spatial energy %s didn't have well", |
1215 |
|
|
me, pctx->energySpecR->energy->name); |
1216 |
|
|
return 1; |
1217 |
|
|
} |
1218 |
|
|
if (pullEnergyBspln != pctx->energySpecS->energy) { |
1219 |
|
|
biffAddf(PULL, "%s: want %s energy along scale, not %s", me, |
1220 |
|
|
pullEnergyBspln->name, |
1221 |
|
|
pctx->energySpecS->energy->name); |
1222 |
|
|
return 1; |
1223 |
|
|
} |
1224 |
|
|
} else { |
1225 |
|
|
biffAddf(PULL, "%s: need %s or %s inter type, not %s", me, |
1226 |
|
|
airEnumStr(pullInterType, pullInterTypeAdditive), |
1227 |
|
|
airEnumStr(pullInterType, pullInterTypeSeparable), |
1228 |
|
|
airEnumStr(pullInterType, pctx->interType)); |
1229 |
|
|
return 1; |
1230 |
|
|
} |
1231 |
|
|
pointNum = pullPointNumber(pctx); |
1232 |
|
|
if (!pointNum) { |
1233 |
|
|
biffAddf(PULL, "%s: had no points!", me); |
1234 |
|
|
return 1; |
1235 |
|
|
} |
1236 |
|
|
|
1237 |
|
|
mop = airMopNew(); |
1238 |
|
|
strdd = AIR_CALLOC(pointNum, double); |
1239 |
|
|
airMopAdd(mop, strdd, airFree, airMopAlways); |
1240 |
|
|
gmag = AIR_CALLOC(pointNum, double); |
1241 |
|
|
airMopAdd(mop, gmag, airFree, airMopAlways); |
1242 |
|
|
if (!(strdd && gmag)) { |
1243 |
|
|
biffAddf(PULL, "%s: couldn't alloc two buffers of %u doubles", |
1244 |
|
|
me, pointNum); |
1245 |
|
|
airMopError(mop); |
1246 |
|
|
return 1; |
1247 |
|
|
} |
1248 |
|
|
|
1249 |
|
|
task = pctx->task[0]; |
1250 |
|
|
pointIdx = 0; |
1251 |
|
|
deltaScale = pctx->bboxMax[3] - pctx->bboxMin[3]; |
1252 |
|
|
deltaScale *= _PULL_STRENGTH_ENERGY_DELTA_SCALE; |
1253 |
|
|
for (binIdx=0; binIdx<pctx->binNum; binIdx++) { |
1254 |
|
|
unsigned int pidx; |
1255 |
|
|
bin = pctx->bin + binIdx; |
1256 |
|
|
for (pidx=0; pidx<bin->pointNum; pidx++) { |
1257 |
|
|
double str[3], _ss, _gr; |
1258 |
|
|
point = bin->point[pidx]; |
1259 |
|
|
point->pos[3] += deltaScale; |
1260 |
|
|
pullProbe(task, point); |
1261 |
|
|
str[2] = pullPointScalar(pctx, point, pullInfoStrength, |
1262 |
|
|
NULL, NULL); |
1263 |
|
|
point->pos[3] -= 2*deltaScale; |
1264 |
|
|
pullProbe(task, point); |
1265 |
|
|
str[0] = pullPointScalar(pctx, point, pullInfoStrength, |
1266 |
|
|
NULL, NULL); |
1267 |
|
|
point->pos[3] += deltaScale; |
1268 |
|
|
pullProbe(task, point); |
1269 |
|
|
str[1] = pullPointScalar(pctx, point, pullInfoStrength, |
1270 |
|
|
NULL, NULL); |
1271 |
|
|
_ss = (str[0] - 2*str[1] + str[2])/(deltaScale*deltaScale); |
1272 |
|
|
if (_ss < 0.0) { |
1273 |
|
|
_gr = (str[2] - str[0])/(2*deltaScale); |
1274 |
|
|
_gr = AIR_ABS(_gr); |
1275 |
|
|
strdd[pointIdx] = _ss; |
1276 |
|
|
gmag[pointIdx] = _gr; |
1277 |
|
|
pointIdx++; |
1278 |
|
|
} |
1279 |
|
|
} |
1280 |
|
|
} |
1281 |
|
|
if (!pointIdx) { |
1282 |
|
|
biffAddf(PULL, "%s: no points w/ 2nd deriv of strn wrt scale < 0", me); |
1283 |
|
|
airMopError(mop); |
1284 |
|
|
return 1; |
1285 |
|
|
} |
1286 |
|
|
|
1287 |
|
|
/* resetting pointNum to actual number of points used */ |
1288 |
|
|
pointNum = pointIdx; |
1289 |
|
|
/* learn meanGmag, with sqrt() sneakiness to discount high gmags */ |
1290 |
|
|
meanGmag = 0.0; |
1291 |
|
|
for (pointIdx=0; pointIdx<pointNum; pointIdx++) { |
1292 |
|
|
meanGmag += sqrt(gmag[pointIdx]); |
1293 |
|
|
} |
1294 |
|
|
meanGmag /= pointNum; |
1295 |
|
|
meanGmag *= meanGmag; |
1296 |
|
|
/* learn meanStrdd with a Gaussian weight on gmag; we want |
1297 |
|
|
to give more weight to the strdds that are near maximal strength |
1298 |
|
|
(hence 1st derivative near zero) */ |
1299 |
|
|
meanStrdd = wghtSum = 0.0; |
1300 |
|
|
for (pointIdx=0; pointIdx<pointNum; pointIdx++) { |
1301 |
|
|
/* the "meanGmag/8" allowed the gamma learned from a |
1302 |
|
|
cone dataset immediately post-initialization to |
1303 |
|
|
nearly match the gamma learned post-phase-2 */ |
1304 |
|
|
wght = airGaussian(gmag[pointIdx], 0.0, meanGmag/8); |
1305 |
|
|
wghtSum += wght; |
1306 |
|
|
meanStrdd += wght*strdd[pointIdx]; |
1307 |
|
|
} |
1308 |
|
|
meanStrdd /= wghtSum; |
1309 |
|
|
|
1310 |
|
|
scl = pctx->sysParm.radiusScale; |
1311 |
|
|
if (pullInterTypeAdditive == pctx->interType) { |
1312 |
|
|
/* want to satisfy str''(s) = enr''(s) |
1313 |
|
|
** ==> -gamma*strdd = 2*beta/(radiusScale)^2 |
1314 |
|
|
** ==> gamma = -2*beta/(strdd*(radiusScale)^2) |
1315 |
|
|
** (beta = 1) ==> gamma = -2/(strdd*(radiusScale)^2) |
1316 |
|
|
** NOTE: The difference from what is in the paper is a factor of 2, |
1317 |
|
|
** and the ability to include the influence of beta |
1318 |
|
|
*/ |
1319 |
|
|
beta = (pctx->flag.useBetaForGammaLearn |
1320 |
|
|
? pctx->sysParm.beta |
1321 |
|
|
: 1.0); |
1322 |
|
|
pctx->sysParm.gamma = -2*beta/(meanStrdd*scl*scl); |
1323 |
|
|
} else if (pullInterTypeSeparable == pctx->interType) { |
1324 |
|
|
/* want to satisfy str''(s) = enr''(s); wellY < 0 |
1325 |
|
|
** ==> gamma*strdd = wellY*8/(radiusScale)^2 |
1326 |
|
|
** gamma = wellY*8/(strdd*(radiusScale)^2) |
1327 |
|
|
*/ |
1328 |
|
|
pctx->sysParm.gamma = wellY*8/(meanStrdd*scl*scl); |
1329 |
|
|
pctx->sysParm.gamma *= pctx->sysParm.separableGammaLearnRescale; |
1330 |
|
|
} else { |
1331 |
|
|
biffAddf(PULL, "%s: sorry %s inter type unimplemented", me, |
1332 |
|
|
airEnumStr(pullInterType, pctx->interType)); |
1333 |
|
|
airMopError(mop); |
1334 |
|
|
return 1; |
1335 |
|
|
} |
1336 |
|
|
if (pctx->verbose) { |
1337 |
|
|
printf("%s: learned gamma %g\n", me, pctx->sysParm.gamma); |
1338 |
|
|
} |
1339 |
|
|
|
1340 |
|
|
airMopOkay(mop); |
1341 |
|
|
return 0; |
1342 |
|
|
} |
1343 |
|
|
|