1 /* optdesign.c
2 
3    Originally written by Frederic Bois
4    16 August 1997
5 
6    Copyright (c) 1997-2017 Free Software Foundation, Inc.
7 
8    This file is part of GNU MCSim.
9 
10    GNU MCSim is free software; you can redistribute it and/or
11    modify it under the terms of the GNU General Public License
12    as published by the Free Software Foundation; either version 3
13    of the License, or (at your option) any later version.
14 
15    GNU MCSim is distributed in the hope that it will be useful,
16    but WITHOUT ANY WARRANTY; without even the implied warranty of
17    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18    GNU General Public License for more details.
19 
20    You should have received a copy of the GNU General Public License
21    along with GNU MCSim; if not, see <http://www.gnu.org/licenses/>
22 */
23 
24 #include <assert.h>
25 #include <stdlib.h>
26 #include <string.h>
27 
28 #include "optdsign.h"
29 #include "hungtype.h"
30 #include "lexerr.h"
31 #include "matutil.h"
32 #include "mh.h"
33 #include "simmonte.h"
34 #include "yourcode.h"
35 
36 enum {Shannon, Var_Reduction}; /* optimization criterion */
37 
38 
39 /* ----------------------------------------------------------------------------
40    InitOptArrays
41 
42    Count Data statements and initialize some arrays.
43 */
44 
InitOptArrays(PANALYSIS panal,int ** piDesign_mask,long * pnDesignPts,double *** pdY,long * pnPreds,long * pnStartDecisionPts,double ** pdVariance,double ** pdIR,long nSims)45 void InitOptArrays (PANALYSIS panal, int **piDesign_mask,
46                     long *pnDesignPts, double ***pdY, long *pnPreds,
47                     long *pnStartDecisionPts, double **pdVariance,
48                     double **pdIR, long nSims)
49 {
50   BOOL bFound;
51   int i, j, k;
52   OUTSPEC *pos;
53 
54   /* Count the total number of predictions requested and the data points
55      specified (a prediction+data pair defines a design points */
56   *pnPreds = *pnDesignPts = 0;
57   for (i = 0; i < panal->expGlobal.iExp; i++) {
58     pos = &panal->rgpExps[i]->os;
59     bFound = FALSE;
60     for (j = 0; j < pos->nOutputs; j++) {
61       for (k = 0; k < pos->pcOutputTimes[j]; k++) {
62 
63         /* if a Data statement exists that's a design point */
64         if (pos->prgdDataVals) {
65           (*pnDesignPts)++;
66           bFound = TRUE;
67         }
68         (*pnPreds)++;
69       } /* for k */
70     } /* for j */
71     if (bFound)
72       /* Simulation i contained a data statement, decision points start at
73          least in the next Simulation */
74       *pnStartDecisionPts = *pnPreds;
75   } /* for i */
76 
77   if (*pnDesignPts == 0) { /* no design points ? */
78     printf("Error: you must provide Data Statements ");
79     printf("for at least one Simulation to define design points - Exiting.\n");
80     exit(0);
81   }
82 
83   if (*pnPreds == *pnDesignPts) { /* no pure predictions ? */
84     printf ("Error: you must provide at least one Simulation ");
85     printf ("without Data Statements for utility computations - Exiting.\n");
86     exit(0);
87   }
88 
89   /* allocate the piDesign_mask array: one per design point */
90   /* allocate the pdVariance array: one per design point */
91   /* allocate the pdIR array: one per iteration */
92   /* allocate the pdY matrix */
93   if (( !(*piDesign_mask = InitiVector (*pnDesignPts))) ||
94       ( !(*pdVariance    = InitdVector (*pnDesignPts))) ||
95       ( !(*pdIR          = InitdVector (nSims)))        ||
96       ( !(*pdY           = InitdMatrix (nSims, *pnPreds))))
97     ReportError (NULL, RE_OUTOFMEM | RE_FATAL, "InitOptArrays", NULL);
98 
99 } /* InitOptArrays */
100 
101 
102 /* ----------------------------------------------------------------------------
103    OpenOptFiles
104 
105    Opens the output file.
106 */
107 
OpenOptFiles(PANALYSIS panal)108 void OpenOptFiles (PANALYSIS panal)
109 {
110   /* take care of the output file first */
111 
112   /* use command line spec if given */
113   if (panal->bCommandLineSpec)
114     panal->gd.szGout = panal->szOutfilename;
115 
116   /* use default name if none given */
117   else if (!(panal->gd.szGout))
118     panal->gd.szGout = "simopt.default.out";
119 
120   if (!(panal->gd.pfileOut)
121       && !(panal->gd.pfileOut = fopen (panal->gd.szGout, "w")))
122     ReportError (NULL, RE_FATAL | RE_CANNOTOPEN,
123                  panal->gd.szGout, "[in OpenOptFiles()]");
124 
125 } /* OpenOptFiles */
126 
127 
128 /* ----------------------------------------------------------------------------
129    WriteOutHeader
130 
131    Prints a tabulated header at the top of the output file.
132 */
133 
WriteOutHeader(PANALYSIS panal,int criterion)134 void WriteOutHeader (PANALYSIS panal, int criterion)
135 {
136   int i, j, k;
137   OUTSPEC *pos;
138 
139   fprintf (panal->gd.pfileOut, "iter\t");
140 
141   for (i = 0; i < panal->expGlobal.iExp; i++) {
142     pos = &panal->rgpExps[i]->os;
143     for (j = 0; j < pos->nOutputs; j++) {
144       for (k = 0; k < pos->pcOutputTimes[j]; k++) {
145         /* if a Data statement exists... */
146         if (pos->prgdDataVals)
147           fprintf (panal->gd.pfileOut, "T%g\t", pos->prgdOutputTimes[j][k]);
148       }
149     }
150   }
151 
152   fprintf (panal->gd.pfileOut, "Chosen\t");
153 
154   if (criterion == Var_Reduction)
155     fprintf (panal->gd.pfileOut, "Variance\tSD\tUtility\n");
156 
157   fflush (panal->gd.pfileOut);
158 
159 } /* WriteOutHeader */
160 
161 
162 /* ----------------------------------------------------------------------------
163    SetupLikes
164 
165    Scan each Monte Carlo variable to find likelihoods and set
166    their pdParms to data and output arrays. pdVal and dVal are not set.
167 */
168 
SetupLikes(PANALYSIS panal,long nPreds,PMCVAR ** pLikes)169 void SetupLikes (PANALYSIS panal, long nPreds, PMCVAR **pLikes)
170 {
171   BOOL bFound, bLikeFound;
172   long i, j, k, m, n;
173   long nPts = 0;
174   OUTSPEC *pos;
175   PMCVAR pMCVar;
176   PMONTECARLO pMC = &panal->mc;
177 
178   /* Create an array of pointers to MCVars, one for each design point */
179   if (!(*pLikes = (PMCVAR*) malloc (nPreds * sizeof(PMCVAR))))
180     ReportError (NULL, RE_OUTOFMEM | RE_FATAL, "SetupLikes", NULL);
181 
182   /* Scan all design points, find the likelihood associated with each one */
183   for (i = 0; i < panal->expGlobal.iExp; i++) {
184 
185     pos = &panal->rgpExps[i]->os;
186 
187     for (j = 0; j < pos->nOutputs; j++) {
188 
189       for (k = 0; k < pos->pcOutputTimes[j]; k++) {
190 
191         /* Allocate space for an MCVar likelihood specification */
192         if (!((*pLikes)[nPts] = (PMCVAR) malloc (sizeof(MCVAR))))
193           ReportError (NULL, RE_OUTOFMEM | RE_FATAL, "SetupLikes", NULL);
194 
195         /* If a Data statement exists that's a design point */
196         if (pos->prgdDataVals) {
197 
198           /* Find the likelihood statement associated with the current
199              output variable */
200           bLikeFound = FALSE;
201           m = pMC->nSetParms;
202           while (!bLikeFound) {
203             bLikeFound = (pMC->rgpMCVar[m]->hvar == pos->phvar_out[j]);
204             if (!bLikeFound) m++;
205           }
206 
207           if (bLikeFound) { /* Set pointers of MCVar */
208 
209             pMCVar = pMC->rgpMCVar[m];
210 
211             /* You can use parameters defined in the read-in list in
212                Likelihoods. You cannot yet add Distrib statements (that
213                would require more accounting, checking and sampling */
214             /* Fetch pointers to the parents in read-in list */
215             SetParents (pMC, 0);
216 
217             /* Set the data and predictions pdParms of pMCVar */
218             for (m = 0; m < 4; m++) {
219 
220               if (pMCVar->iParmType[m] == MCVP_PRED) { /* it's an output */
221                 /* Set pdParm[m] to an output, first find it */
222                 bFound = FALSE;
223                 n = 0;
224                 while ((n < pos->nOutputs) && (!bFound)) {
225                   bFound = (pMCVar->hParm[m] == pos->phvar_out[n]);
226                   if (!bFound) n++;
227                 }
228                 if (bFound) {
229                   pMCVar->pdParm[m] = &(pos->prgdOutputVals[n][k]);
230                 }
231                 else { /* no corresponding Print statement found: error */
232                   printf ("Error: missing Print statement for parameter "
233                           "number %ld of %s distribution - Exiting.\n\n",
234                           j, pMCVar->pszName);
235                   exit (0);
236                 }
237               } /* end if MCVP_PRED */
238 
239               else if (pMCVar->iParmType[m] == MCVP_DATA) { /* it's data */
240                 /* Set pdParm[m] to a data array, first find it */
241                 bFound = FALSE;
242                 n = 0;
243                 while ((n < pos->nData) && (!bFound)) {
244                   bFound = (pMCVar->hParm[m] == pos->phvar_dat[n]);
245                   if (!bFound) n++;
246                 }
247                 if (bFound) {
248                   pMCVar->pdParm[m] = &(pos->prgdDataVals[n][k]);
249                 }
250                 else { /* no Data found: error */
251                   printf ("Error: no Data for %s in Simulation %ld "
252                           "- Exiting.\n\n", pMCVar->pszName, i);
253                   exit (0);
254                 }
255               } /* end if MCVP_DATA */
256             } /* for m */
257           } /* end if bLikeFound */
258           else {
259             /* No likelihood statement found for the current output: error */
260             printf ("Error: missing Likelihood for %s - Exiting.\n\n",
261                     pos->pszOutputNames[j]);
262             exit (0);
263           }
264 
265           /* Copy the MCVar created */
266           memcpy ((*pLikes)[nPts], pMCVar, sizeof (MCVAR));
267 
268         } /* if data */
269 
270         else { /* no data: give a null likelihood spec */
271           (*pLikes)[nPts] = NULL;
272         }
273 
274         /* Increment likelihood array index */
275         nPts = nPts + 1;
276 
277       } /* for k */
278     } /* for j */
279   } /* for i */
280 
281 } /* SetupLikes */
282 
283 
284 /* ----------------------------------------------------------------------------
285    Estimate_y
286 
287    Calculates y[] for the given conditions by running the model.
288    y[] is then flattened in the pdY array.
289 
290    Note that pdY may not be completely initialized by this routine if it
291    is passed uninitialized.
292 */
293 
Estimate_y(PANALYSIS panal,double * pdTheta,double * pdY)294 int Estimate_y (PANALYSIS panal, double *pdTheta, double *pdY)
295 {
296   int cNPred = 0;
297   int i, j, k;
298   OUTSPEC *pos;
299 
300   /* Run the PBPK model for each experiment assigned to the subject specified
301    */
302   for (i = 0; i < panal->expGlobal.iExp; i++) {
303     InitModel ();
304 
305     /* global modifications */
306     ModifyParms (panal->expGlobal.plistParmMods);
307 
308     /* set params to pdTheta values */
309     SetParms (panal->mc.nSetParms, panal->mc.rghvar, pdTheta);
310 
311     /* set the Mods for this exp */
312     ModifyParms (panal->rgpExps[i]->plistParmMods);
313 
314     if (!DoOneExperiment (panal->rgpExps[i])) {
315       /* Error */
316       printf ("Warning: Can't estimate y with parameters:\n");
317       WriteArray (stdout, panal->mc.nSetParms, pdTheta);
318       fputc('\n', stdout);
319       return 0;
320     } /* if */
321 
322     /* link pdY to outputs */
323     pos = &panal->rgpExps[i]->os;
324     for (j = 0; j < pos->nOutputs; j++) {
325       for (k = 0; k < pos->pcOutputTimes[j]; k++) {
326         pdY[cNPred] = pos->prgdOutputVals[j][k];
327         cNPred++;
328       } /* for k */
329     } /* for j */
330 
331   } /* for i */
332 
333   return 1;
334 
335 } /* Estimate_y */
336 
337 
338 /* ----------------------------------------------------------------------------
339  ReadAndSimulate
340 
341    Initialize arrays and reads a parameter sample, obtained for example
342    from a previous MCMC sample. Run the structural model, simulate data
343    and output the loglikelihood of each design point (Outputs having
344    associated Data statements) in pdY. For non-design points of output
345    pdY contains just the output value (not its log-likelihood).
346 
347 */
348 
ReadAndSimulate(PANALYSIS panal,long nSetParms,double ** pdY,long nPreds,PMCVAR * pLikes,long nSims)349 void ReadAndSimulate (PANALYSIS panal, long nSetParms,
350                       double **pdY, long nPreds, PMCVAR *pLikes, long nSims)
351 {
352   register char c;
353   long lDummy, iter = 0;
354   long j;
355   PDOUBLE pdTheta = NULL;
356   PDOUBLE pdTheta_0 = NULL;
357   PDOUBLE pdData = NULL;
358   PDOUBLE pdData_old = NULL;
359   FILE *pfileRestart = panal->gd.pfileRestart;
360 
361   /* Temporary space allocation for model parameters and simulated data */
362   if ( !(pdTheta    = InitdVector (nSetParms)) ||
363        !(pdTheta_0  = InitdVector (nSetParms)) ||
364        !(pdData_old = InitdVector (nPreds))    ||
365        !(pdData     = InitdVector (nPreds)))
366     ReportError (NULL, RE_OUTOFMEM | RE_FATAL, "ReadAndSimulate", NULL);
367 
368   /* open the restart file */
369   if (!(pfileRestart)
370       && !(pfileRestart = fopen (panal->gd.szGrestart, "r")))
371     ReportError (NULL, RE_FATAL | RE_CANNOTOPEN,
372                  panal->gd.szGrestart, "[in ReadAndSimulate()]");
373 
374   /* Skip the first line. This allows a MC output file to be used
375      directly as a restart file. */
376   do { c = getc(pfileRestart); } while (c != '\n');
377 
378   /* Keep reading lines as long as iter < nSims and we have not reached
379      the end of the file.
380      Also keep incrementing the global iteration counter, iter:  */
381   while ((iter < nSims) &&
382          ( !(feof(pfileRestart) ||
383            (fscanf(pfileRestart, "%ld", &lDummy) == EOF)))) {
384 
385     /* Read model parameters in pdTheta */
386     for (j = 0; j < nSetParms; j++) {
387       if (!fscanf (pfileRestart, "%lg", &(pdTheta[j])))
388         ReportError (NULL, RE_READERROR | RE_FATAL, panal->gd.szGrestart, NULL);
389       else
390         panal->mc.rgpMCVar[j]->dVal = pdTheta[j];
391     }
392 
393     /* Throw away remainder of line */
394     do { c = getc(pfileRestart); } while (c != '\n');
395 
396     /* For all experiments, compute the predictions for the parameter vector */
397     Estimate_y (panal, pdTheta, pdY[iter]);
398 
399     if (iter == 0) { /* first simulation, just make predictions */
400       for (j = 0; j < nSetParms; j++) {
401         pdTheta_0[j] = pdTheta[j]; /* save parameter vector for reuse at end */
402       }
403       for (j = 0; j < nPreds; j++) {
404         if (pLikes[j] != NULL) {
405           CalculateOneMCParm (pLikes[j]);
406           pdData_old[j] = pLikes[j]->dVal;
407         }
408       }
409     }
410     else { /* other iterations */
411       /* Simulate data */
412       for (j = 0; j < nPreds; j++) {
413         if (pLikes[j] != NULL) {
414           CalculateOneMCParm (pLikes[j]);
415           pdData[j] = pLikes[j]->dVal;
416         }
417       }
418 
419       /* Compute the data likelihood with the previous iteration data vector */
420       for (j = 0; j < nPreds; j++)
421         if (pLikes[j] != NULL) {
422           pLikes[j]->dVal = pdData_old[j];
423           pdY[iter][j] = LnDensity (pLikes[j], panal);
424 
425           /* Update old data vector */
426           pdData_old[j] = pdData[j];
427         }
428 
429       if (iter == nSims - 1) { /* last iteration: process the first one also */
430         /* Recompute the predictions to be up to date for all outputs because
431            likelihood parameters may point to panal->os internal variables */
432         Estimate_y (panal, pdTheta_0, pdY[0]);
433         for (j = 0; j < nPreds; j++)
434           if (pLikes[j] != NULL) {
435             pLikes[j]->dVal = pdData_old[j];
436             pdY[0][j] = LnDensity (pLikes[j], panal);
437           }
438       }
439     } /* end else iter */
440 
441     /* Increment iter */
442     iter++;
443 
444   } /* end while */
445 
446   if (iter < nSims) {
447     printf ("\nError: The number of lines in file %s is less than\n",
448 	    panal->gd.szGrestart);
449     printf ("       the number of lines to read (%ld) - Exiting\n", nSims);
450     exit (0);
451   }
452 
453   fclose (pfileRestart);
454 
455 } /* ReadAndSimulate */
456 
457 
458 /* ----------------------------------------------------------------------------
459    Do_Importance_Ratios
460 
461    Compute the expected likelihood of nDesignPts and forms the
462    importance ratios by scaling them to their sum.
463    The prediction array MUST contain the log-likelihood of individual data
464    points.
465 
466    Uses piDesign_mask to select the design points to consider.
467 */
468 
Do_Importance_Ratios(double ** pdY,PMCVAR * pLikes,long nSims,long nPreds,long nDesignPts,int * piDesign_mask,int nDesignPt_tried,double * pdIR)469 void Do_Importance_Ratios (double **pdY, PMCVAR *pLikes, long nSims,
470                            long nPreds, long nDesignPts, int *piDesign_mask,
471                            int nDesignPt_tried, double *pdIR)
472 {
473   long i, j, k;
474   double dSumL = 0;
475   BOOL bOn;
476 
477   for (k = 0; k < nSims; k++) {
478     pdIR[k] = 0;
479     j = 0;
480     for (i = 0; i < nPreds; i++) {
481       if (pLikes[i] != NULL) {
482         if (j == nDesignPt_tried) /* invert the current mask */
483           bOn = ! piDesign_mask[j];
484         else /* straigth mask */
485           bOn = piDesign_mask[j];
486 
487         /* Form the log-likelihood for all design experiments */
488         if (bOn)
489           pdIR[k] = pdIR[k] + pdY[k][i];
490 
491         j++;
492       } /* if pLikes */
493     } /* for i */
494 
495     pdIR[k] = exp(pdIR[k]);
496     dSumL = dSumL + pdIR[k];
497 
498   } /* for k */
499 
500   for (k = 0; k < nSims; k++)
501     pdIR[k] = pdIR[k] / dSumL;
502 
503 } /* Do_Importance_Ratios */
504 
505 
506 /* ----------------------------------------------------------------------------
507    DoVariance
508 
509    Compute the total variance (after log transformation) of elements of a
510    matrix, considering importance ratios.
511    Only part of the matrix (from istart to ifinish) is used.
512 */
513 
DoVariance(long nDim,double * pdIR,double ** pdX,long istart,long ifinish)514 double DoVariance (long nDim, double *pdIR, double **pdX,
515                    long istart, long ifinish)
516 {
517   long i, j;
518   register double ave, ss, dTmp;
519 
520   ss = 0;
521 
522   /* for all predictions (X between istart and ifinish) */
523   for (i = istart; i < ifinish; i++) {
524     ave = 0;
525     /* Compute the average pdX */
526     for (j = 0; j < nDim; j++) {
527       ave = ave + pdIR[j] * log(pdX[j][i]);
528     }
529 
530     /* Compute the SS over pdX */
531     for (j = 0; j < nDim; j++) {
532       dTmp = log(pdX[j][i]) - ave;
533       ss = ss + pdIR[j] * dTmp * dTmp;
534     }
535   }
536 
537   return (ss / (double) (ifinish - istart));
538 
539 } /* DoVariance */
540 
541 
542 /* ----------------------------------------------------------------------------
543    Compute_utility
544 
545    Utilities can be computed here: totally arbitrary and unused for now
546 */
547 
Compute_utility(long nDesignPts,int * piDesign_mask,double * dUtility)548 void Compute_utility (long nDesignPts, int *piDesign_mask, double *dUtility)
549 {
550   int nPts = 0;
551   int i;
552 
553   for (i = 0; i < nDesignPts; i++)
554     if (piDesign_mask[i]) nPts++;
555 
556   /* Let say that cost is 2 monetary units per design point */
557   *dUtility = -2 * nPts;
558 
559 } /* Compute_utility */
560 
561 
562 /* ----------------------------------------------------------------------------
563    WriteOptimOut
564 
565    writes to the output file, print only the significant portions.
566 */
567 
WriteOptimOut(PANALYSIS panal,long iter,long nDesignPts,int criterion,double * pdVariance,int * piDesign_mask,long iCrit,double dCrit,double dUtility)568 void WriteOptimOut (PANALYSIS panal, long iter, long nDesignPts,
569                     int criterion, double *pdVariance, int *piDesign_mask,
570                     long iCrit, double dCrit, double dUtility)
571 {
572   long i;
573 
574   fprintf (panal->gd.pfileOut, "%ld\t", iter);
575 
576   if (iCrit < nDesignPts) { /* if iCrit is usefully defined */
577     for (i = 0; i < nDesignPts; i++) {
578       if ((&panal->mc)->style == forward) {
579         if ((i == iCrit) || !(piDesign_mask[i]))
580           fprintf (panal->gd.pfileOut, "%g\t", pdVariance[i]);
581         else
582           fprintf (panal->gd.pfileOut, "0\t");
583       }
584       else { /* backward style, the mask needs to be inverted */
585         if (!(piDesign_mask[i]))
586           fprintf (panal->gd.pfileOut, "0\t");
587         else
588           fprintf (panal->gd.pfileOut, "%g\t", pdVariance[i]);
589       }
590     } /* end for */
591 
592     fprintf (panal->gd.pfileOut, "%ld\t", iCrit+1);
593   }
594   else
595     for (i = 0; i <= nDesignPts; i++)
596       fprintf (panal->gd.pfileOut, "0\t");
597 
598   if (criterion == Var_Reduction)
599     fprintf (panal->gd.pfileOut, "%g\t%g\t%g\n", dCrit, sqrt(dCrit), dUtility);
600 
601   fflush (panal->gd.pfileOut);
602 
603 } /* WriteOptimOut */
604 
605 
606 /* ----------------------------------------------------------------------------
607    Importance_Resample
608 
609    Does just that, updating pIndex1, picking from pIndex0, pdL is destroyed.
610 */
611 
Importance_Resample(long nSims,long * pIndex0,long * pIndex1,long * plDrawn,double * pdL,double dSumL)612 void Importance_Resample (long nSims, long *pIndex0, long *pIndex1,
613                           long *plDrawn, double *pdL, double dSumL)
614 {
615   long i, j;
616 
617   /* do the weights */
618   for (i = 0; i < nSims; i++)
619     pdL[i] = pdL[i] / dSumL;
620 
621   j = 0;
622   do {
623     /* randomly pick a vector among the nSims available. No risk of
624        overflow because Randoms() is between 0 and 1 */
625     i = (long) floor (Randoms() * nSims);
626 
627     if (Randoms() < pdL[i]) { /* accept */
628       pIndex1[j] = pIndex0[i];
629       j++;
630       plDrawn[pIndex0[i]]++;
631     }
632   }
633   while (j < nSims);
634 
635 } /* Importance_Resample */
636 
637 
638 /* ----------------------------------------------------------------------------
639    CloseOptFiles
640 
641    Closes output files associated with the design optimizer.
642    The restart file has already been closed by the ReadAndSimulate
643 
644 */
645 
CloseOptFiles(PANALYSIS panal)646 void CloseOptFiles (PANALYSIS panal)
647 {
648   if (panal->gd.pfileOut) {
649     fclose (panal->gd.pfileOut);
650     printf ("\nWrote results to \"%s\"\n", panal->gd.szGout);
651   }
652 
653 } /* CloseOptFiles */
654 
655 
656 /* ----------------------------------------------------------------------------
657    DoOptimalDesign
658 
659    Core routine of the experimental design optimizer
660 */
661 
DoOptimalDesign(PANALYSIS panal)662 void DoOptimalDesign (PANALYSIS panal)
663 {
664   PGIBBSDATA  pgd = &panal->gd;
665   PMONTECARLO pmc = &panal->mc;
666 
667   long i, j;
668   long iBest = 0;
669   long dim;
670   long nDesignPts;            /* # of data points in a data set */
671   long nPreds;                /* # of predicted values */
672   long nSims = pmc->nRuns;    /* # of model parametrizations tested */
673   int  *piDesign_mask;        /* current mask for likelihood computation */
674   long nDesignPt_tried;       /* index of the currently tried time point */
675   long nStartDecisionPts;     /* index to the start of decision points */
676   int  criterion;             /* either Shannon's or variance reduction */
677   double dBest = 0;           /* best criterion */
678   double dUtility = 0;        /* total utility of a design */
679   double *pdIR;               /* array of importance ratios (dim: nSims) */
680   double *pdVariance;         /* pred. variances. associated to a design pt */
681   double **pdY;               /* predictions, then likelihoods */
682   PMCVAR *pLikes;             /* array of likelihood specifications */
683 
684   /* criterion can be Var_Reduction or Shannon */
685   criterion = Var_Reduction;
686   if (criterion != Var_Reduction) {
687     printf ("Oooops, Shannon not implemented - exiting\n");
688     exit (0);
689   }
690 
691   /* announce the work to be done */
692   printf ("\nDoing analysis - Optimal Design %s %s - %d experiment%c\n",
693           (pmc->style == forward ? "forward" : "backward"),
694           (criterion == Var_Reduction ? "variance reduction" : "Shannon"),
695           panal->expGlobal.iExp, (panal->expGlobal.iExp > 1 ? 's' : ' '));
696 
697   /* open restart and output files */
698   OpenOptFiles (panal);
699 
700   /* Initialize the design-points and predictions arrays */
701   InitOptArrays (panal, &piDesign_mask, &nDesignPts, &pdY, &nPreds,
702                  &nStartDecisionPts, &pdVariance, &pdIR, nSims);
703 
704   /* Associate predictions to MCVars likelihood records */
705   SetupLikes (panal, nPreds, &pLikes);
706 
707   /* Read in the parameter samples and simulate predictions */
708   if (!(pgd->szGrestart)) {
709     /* error: we must have a starting prior sample */
710     printf ("Error: there must be a parameter sample file - Exiting\n");
711     exit (0);
712   }
713   else {
714     /* read the starting values in the order they are printed and
715        close the file when finished */
716     ReadAndSimulate (panal, pmc->nSetParms, pdY, nPreds, pLikes, nSims);
717   }
718 
719   /* Write the header line of the output file */
720   WriteOutHeader (panal, criterion);
721 
722   /* Turn experimental points on (backward trials) or off (forward trials) */
723   for (i = 0; i < nDesignPts; i++)
724     piDesign_mask[i] = !(pmc->style == forward);
725 
726   /* Start the computation */
727   if (criterion == Var_Reduction) {
728 
729     if (pmc->style == backward) { /* all points included */
730 
731       /* Set nDesignPt_tried at a value beyond the array bounds so that it is
732          not found and does not interfere in the next call to Likelihood */
733       nDesignPt_tried = nDesignPts + 1;
734 
735       /* Compute the importance ratios of each parameter set */
736       Do_Importance_Ratios (pdY, pLikes, nSims, nPreds, nDesignPts,
737                             piDesign_mask, nDesignPt_tried, pdIR);
738 
739       /* Compute the total variance of the pure predictions (decision
740          points) */
741       dBest = DoVariance (nSims, pdIR, pdY, nStartDecisionPts, nPreds);
742     }
743     else { /* forward style is simple */
744       for (j = 0; j < nSims; j++)
745         pdIR[j] = 1 / (double) nSims;
746 
747       dBest = DoVariance (nSims, pdIR, pdY, nStartDecisionPts, nPreds);
748     }
749   }
750 
751   iBest = nDesignPts + 1; /* out of bounds, undefined */
752 
753   /* Compute_utility (nDesignPts, piDesign_mask, &dUtility); */
754 
755   /* output the first line ... */
756   WriteOptimOut (panal, 0, nDesignPts, criterion, pdVariance, piDesign_mask,
757                  iBest, dBest, dUtility);
758 
759   /* Start the loop over designs points, only some of the previously
760      computed points will be used */
761   dim = ((pmc->style == backward) ? nDesignPts - 1 : nDesignPts);
762   for (i = 0; i < dim; i++) {
763 
764     /* Reset the criterion for "best" */
765     if (criterion == Var_Reduction)
766       dBest = DBL_MAX;
767 
768     for (j = 0; j < nDesignPts; j++) {
769 
770       /* Try this point, if it is not already accepted or deleted.
771          (style == backward) is 1 in case of backward style and 0 in
772          case of forward style. So if we go backward we skip the point
773          if it is already "off" */
774       if (piDesign_mask[j] == (pmc->style == backward)) {
775 
776         nDesignPt_tried = j; /* try add or remove point j */
777 
778         pdVariance[j] = 0; /* reset */
779 
780         /* Check whether this point is the best of the j tried so far */
781         if (criterion == Var_Reduction) {
782 
783           /* Do an importance reweighting to be able to get the variance
784              reductions */
785           Do_Importance_Ratios (pdY, pLikes, nSims, nPreds, nDesignPts,
786                                 piDesign_mask, nDesignPt_tried, pdIR);
787 
788           /* Compute the total variance of the decision points */
789           pdVariance[j] = DoVariance (nSims, pdIR, pdY, nStartDecisionPts,
790                                       nPreds);
791 
792         } /* if */
793 
794         /* If forward we want to keep the point giving the best reduction,
795            i.e. the lowest variance; if backward we want to remove the point
796            giving the lowest variance augmentation, which is also the lowest
797            variance */
798         if (dBest > pdVariance[j]) {
799           dBest = pdVariance[j];
800           iBest = j;
801         }
802 
803       } /* if piDesign_mask */
804 
805     } /* for j design point */
806 
807     /* set the best point, by inverting it definitely */
808     piDesign_mask[iBest] = !piDesign_mask[iBest];
809 
810     /* Compute_utility (nDesignPts, piDesign_mask, &dUtility); */
811 
812     /* output ... */
813     WriteOptimOut (panal, i+1, nDesignPts, criterion,  pdVariance,
814                    piDesign_mask, iBest, dBest, dUtility);
815 
816     printf ("%ld points design done\n", i+1);
817 
818   } /* for i */
819 
820   /* if style is backward we would still like to know what a zero-point design
821      would do */
822   if (pmc->style == backward) {
823 
824     /* Importance ratios are reinitialized with the natural weights */
825     for (j = 0; j < nSims; j++) pdIR[j] = 1 / (double) nSims;
826 
827     if (criterion == Var_Reduction) {
828       dBest = DoVariance (nSims, pdIR, pdY, nStartDecisionPts, nPreds);
829       j = 0;
830       while (piDesign_mask[j] == 0)
831         j++;
832       iBest = j;
833     }
834 
835     /* Output, with null utility */
836     WriteOptimOut (panal, nDesignPts, nDesignPts, criterion, pdVariance,
837                    piDesign_mask, iBest, dBest, 0);
838 
839     printf ("%ld points design done\n", nDesignPts);
840   }
841 
842   free (piDesign_mask);
843 
844   CloseOptFiles (panal);
845 
846 } /* DoOptimalDesign */
847 
848