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