1 /*
2 Teem: Tools to process and visualize scientific data and images .
3 Copyright (C) 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
27 #define TOP_MARGIN 3
28
29 char *info = ("Sees if \"adhoc\" is inefficient.");
30
31 enum {
32 stepStyleUnknown, /* 0 */
33 stepStyleSmall, /* 1 */
34 stepStyleDescent, /* 2 */
35 stepStyleRandomUniform, /* 3 */
36 stepStyleRandomCool, /* 4 */
37 stepStyleLast
38 };
39 #define STEP_STYLE_MAX 4
40
41 const char *
42 _stepStyleStr[STEP_STYLE_MAX+1] = {
43 "(unknown_style)",
44 "small",
45 "descent",
46 "runiform",
47 "rcool"
48 };
49
50 const airEnum
51 _stepStyle = {
52 "step style",
53 STEP_STYLE_MAX,
54 _stepStyleStr, NULL,
55 NULL,
56 NULL, NULL,
57 AIR_FALSE
58 };
59 const airEnum *const
60 stepStyle = &_stepStyle;
61
62
63 static double
tpimod(double phi)64 tpimod(double phi) {
65
66 if (phi > 0) {
67 phi = fmod(phi, 2*AIR_PI);
68 } else {
69 phi = 2*AIR_PI + fmod(-phi, 2*AIR_PI);
70 }
71 return phi;
72 }
73
74 static double
enrPairwise(double * grad,double me,double she,pullEnergySpec * ensp,double radius)75 enrPairwise(double *grad, double me, double she,
76 pullEnergySpec *ensp, double radius) {
77 double ad, td, rr, *parm, enr, denr,
78 (*eval)(double *, double, const double parm[PULL_ENERGY_PARM_NUM]);
79
80 ad = AIR_ABS(me - she);
81 if (0 == ad) {
82 if (grad) {
83 *grad = 0;
84 }
85 return 0;
86 }
87 td = AIR_ABS(me - (she + 2*AIR_PI));
88 if (td < ad) {
89 she += 2*AIR_PI;
90 ad = td;
91 } else {
92 td = AIR_ABS(me - (she - 2*AIR_PI));
93 if (td < ad) {
94 she -= 2*AIR_PI;
95 ad = td;
96 }
97 }
98 rr = ad/radius;
99 if (rr > 1) {
100 if (grad) {
101 *grad = 0;
102 }
103 return 0;
104 }
105 parm = ensp->parm;
106 eval = ensp->energy->eval;
107 enr = eval(&denr, rr, parm);
108 if (grad) {
109 *grad = denr * airSgn(me - she)/radius;
110 }
111 return enr;
112 }
113
114 static double
enrSingle(double * gradP,double me,unsigned int meIdx,double * pos,unsigned int posNum,pullEnergySpec * ensp,double radius)115 enrSingle(double *gradP, double me, unsigned int meIdx,
116 double *pos, unsigned int posNum,
117 pullEnergySpec *ensp, double radius) {
118 unsigned int ii;
119 double enr, gg, grad;
120
121 enr = 0;
122 grad = 0;
123 me = tpimod(me);
124 for (ii=0; ii<posNum; ii++) {
125 if (ii == meIdx) {
126 continue;
127 }
128 enr += enrPairwise(&gg, me, pos[ii], ensp, radius);
129 grad += gg;
130 }
131 if (gradP) {
132 *gradP = grad;
133 }
134 return enr;
135 }
136
137 static double
enrStdv(double * pos,unsigned int posNum,pullEnergySpec * ensp,double radius)138 enrStdv(double *pos, unsigned int posNum,
139 pullEnergySpec *ensp, double radius) {
140 unsigned int ii;
141 double SS, S, enr;
142
143 SS = S = 0.0;
144 for (ii=0; ii<posNum; ii++) {
145 enr = enrSingle(NULL, pos[ii], ii, pos, posNum, ensp, radius);
146 S += enr;
147 SS += enr*enr;
148 }
149 S /= posNum;
150 SS /= posNum;
151 return sqrt(SS - S*S);
152 }
153
154 static void
runIter(unsigned int iter,double * posOut,int sstyle,const double * posIn,double * step,unsigned int pntNum,pullEnergySpec * ensp,double radius,double cool,double backoff,double creepup)155 runIter(unsigned int iter,
156 double *posOut, int sstyle, const double *posIn, double *step,
157 unsigned int pntNum,
158 pullEnergySpec *ensp, double radius,
159 double cool, double backoff, double creepup) {
160 unsigned int ii;
161 double me, enr0, enr1, frc, tpos;
162 int badstep;
163
164 for (ii=0; ii<pntNum; ii++) {
165 posOut[ii] = posIn[ii];
166 }
167 if (iter < TOP_MARGIN) {
168 /* hack for better seeing initial locations */
169 return;
170 }
171 for (ii=0; ii<pntNum; ii++) {
172 me = posOut[ii];
173 enr0 = enrSingle(&frc, me, ii, posOut, pntNum, ensp, radius);
174 switch(sstyle) {
175 case stepStyleSmall:
176 tpos = me + AIR_AFFINE(0.0, airDrandMT(), 1.0, -radius/10, radius/10);
177 enr1 = enrSingle(NULL, tpos, ii, posOut, pntNum, ensp, radius);
178 if (enr1 < enr0) {
179 me = tpos;
180 }
181 break;
182 case stepStyleDescent:
183 do {
184 tpos = me - step[ii]*frc;
185 enr1 = enrSingle(NULL, tpos, ii, posOut, pntNum, ensp, radius);
186 badstep = enr1 > enr0;
187 if (badstep) {
188 step[ii] *= backoff;
189 }
190 } while (badstep);
191 step[ii] *= creepup;
192 me = tpos;
193 break;
194 case stepStyleRandomUniform:
195 tpos = AIR_AFFINE(0.0, airDrandMT(), 1.0, 0.0, 2*AIR_PI);
196 enr1 = enrSingle(NULL, tpos, ii, posOut, pntNum, ensp, radius);
197 if (enr1 < enr0) {
198 me = tpos;
199 }
200 break;
201 case stepStyleRandomCool:
202 airNormalRand(&tpos, NULL);
203 tpos = me + tpos*cool;
204 enr1 = enrSingle(NULL, tpos, ii, posOut, pntNum, ensp, radius);
205 if (enr1 < enr0) {
206 me = tpos;
207 }
208 break;
209 }
210 posOut[ii] = tpimod(me);
211 }
212 return;
213 }
214
215 int
main(int argc,const char * argv[])216 main(int argc, const char *argv[]) {
217 const char *me;
218 hestOpt *hopt=NULL;
219 airArray *mop;
220
221 char *outS;
222 pullEnergySpec *ensp, *enspCharge;
223 unsigned int pntNum, pntIdx, iter, iterMax, rngSeed, posNum, coolHalfLife;
224 double stdvMin, radius, *pos, *step, backoff, creepup, stepInitial;
225 airArray *posArr;
226 Nrrd *npos;
227 int sstyle, verbose;
228
229 mop = airMopNew();
230 me = argv[0];
231 hestOptAdd(&hopt, "v", "verbose", airTypeInt, 1, 1, &verbose, "0",
232 "verbosity");
233 hestOptAdd(&hopt, "energy", "spec", airTypeOther, 1, 1, &ensp, NULL,
234 "specification of force function to use",
235 NULL, NULL, pullHestEnergySpec);
236 hestOptAdd(&hopt, "ss", "step style", airTypeEnum, 1, 1, &sstyle, NULL,
237 "minimization step style", NULL, stepStyle);
238 hestOptAdd(&hopt, "rad", "radius", airTypeDouble, 1, 1, &radius, NULL,
239 "radius of particle");
240 hestOptAdd(&hopt, "esm", "eng stdv min", airTypeDouble, 1, 1, &stdvMin,
241 "0.05", "minimum stdv of particle energies");
242 hestOptAdd(&hopt, "bo", "backoff", airTypeDouble, 1, 1, &backoff, "0.1",
243 "backoff in gradient descent");
244 hestOptAdd(&hopt, "step", "step", airTypeDouble, 1, 1, &stepInitial, "1.0",
245 "initial step size in gradient descent");
246 hestOptAdd(&hopt, "cu", "creepup", airTypeDouble, 1, 1, &creepup, "1.1",
247 "creepup in gradient descent");
248 hestOptAdd(&hopt, "chl", "cool half life", airTypeUInt, 1, 1, &coolHalfLife,
249 "20", "cool half life");
250 hestOptAdd(&hopt, "np", "# part", airTypeUInt, 1, 1, &pntNum, "42",
251 "# of particles in simulation");
252 hestOptAdd(&hopt, "maxi", "max iter", airTypeUInt, 1, 1, &iterMax, "1000",
253 "max number of iterations");
254 hestOptAdd(&hopt, "seed", "seed", airTypeUInt, 1, 1, &rngSeed, "42",
255 "random number generator seed");
256 hestOptAdd(&hopt, "o", "out", airTypeString, 1, 1, &outS, "-",
257 "output filename");
258 hestParseOrDie(hopt, argc-1, argv+1, NULL,
259 me, info, AIR_TRUE, AIR_TRUE, AIR_TRUE);
260 airMopAdd(mop, hopt, (airMopper)hestOptFree, airMopAlways);
261 airMopAdd(mop, hopt, (airMopper)hestParseFree, airMopAlways);
262
263 airSrandMT(rngSeed);
264 posNum = 0;
265 posArr = airArrayNew(AIR_CAST(void **, &pos),
266 &posNum, pntNum*sizeof(double), iterMax/10);
267 airMopAdd(mop, posArr, (airMopper)airArrayNuke, airMopAlways);
268 step = AIR_CALLOC(pntNum, double);
269 airMopAdd(mop, step, airFree, airMopAlways);
270 for (pntIdx=0; pntIdx<pntNum; pntIdx++) {
271 step[pntIdx] = stepInitial;
272 }
273
274 enspCharge = pullEnergySpecNew();
275 airMopAdd(mop, enspCharge, (airMopper)pullEnergySpecNix, airMopAlways);
276 if (pullEnergySpecParse(enspCharge, "qwell:1")) {
277 char *err;
278 airMopAdd(mop, err = biffGetDone(PULL), airFree, airMopAlways);
279 fprintf(stderr, "%s: problem saving output:\n%s", me, err);
280 airMopError(mop); return 1;
281 }
282
283 for (iter=0; iter<iterMax; iter++) {
284 double stdv, cool, fiter;
285 if (!iter) {
286 airArrayLenIncr(posArr, 1);
287 for (pntIdx=0; pntIdx<pntNum; pntIdx++) {
288 pos[pntIdx] = AIR_AFFINE(0.0, airDrandMT(), 1.0,
289 0.0, 2*AIR_PI);
290 }
291 }
292 fiter = AIR_MAX(0, ((double)iter)-TOP_MARGIN);
293 cool = (AIR_PI/2)*pow(0.5, fiter/coolHalfLife);
294 airArrayLenIncr(posArr, 1);
295 runIter(iter, pos + pntNum*(iter+1), sstyle,
296 pos + pntNum*iter, step, pntNum,
297 ensp, radius, cool, backoff, creepup);
298 stdv = enrStdv(pos + pntNum*(iter+1), pntNum, enspCharge, AIR_PI/2);
299 if (verbose > 1) {
300 fprintf(stderr, "%s: iter %u stdv %g cool %g\n", me, iter, stdv, cool);
301 }
302 if (stdv < stdvMin) {
303 fprintf(stderr, "%s: converged in %u iters (stdv %g < %g)\n", me,
304 iter, stdv, stdvMin);
305 break;
306 }
307 }
308
309 npos = nrrdNew();
310 airMopAdd(mop, npos, (airMopper)nrrdNix, airMopAlways);
311 if (nrrdWrap_va(npos, pos, nrrdTypeDouble, 2,
312 AIR_CAST(size_t, pntNum),
313 AIR_CAST(size_t, posNum))
314 || nrrdSave(outS, npos, NULL)) {
315 char *err;
316 airMopAdd(mop, err = biffGetDone(NRRD), airFree, airMopAlways);
317 fprintf(stderr, "%s: problem saving output:\n%s", me, err);
318 airMopError(mop); return 1;
319 }
320
321
322 airMopOkay(mop);
323 return 0;
324 }
325