source: trunk/optimizer/src/rp_optimizer.c @ 897

Last change on this file since 897 was 897, checked in by mmc, 15 years ago

Improved the Rappture optimization API to include Tcl bindings.
Added standard build/test stuff for Tcl libraries. Created a
plugin hook for optimization libraries like pgapack. The plugin
support still needs some work.

File size: 10.9 KB
Line 
1/*
2 * ----------------------------------------------------------------------
3 *  rp_optimizer
4 *
5 *  This is the C language API for the optimization package in
6 *  Rappture.  It lets you set up an optimization of some fitness
7 *  function with respect to a set of inputs.
8 *
9 * ======================================================================
10 *  AUTHOR:  Michael McLennan, Purdue University
11 *  Copyright (c) 2008  Purdue Research Foundation
12 *
13 *  See the file "license.terms" for information on usage and
14 *  redistribution of this file, and for a DISCLAIMER OF ALL WARRANTIES.
15 * ======================================================================
16 */
17#include <math.h>
18#include <stdlib.h>
19#include <string.h>
20#include "rp_optimizer.h"
21
22static void RpOptimCleanupParam _ANSI_ARGS_((RpOptimParam *paramPtr));
23
24/*
25 * ----------------------------------------------------------------------
26 * RpOptimCreate()
27 *
28 * Used to create the context for an optimization.  Creates an empty
29 * context and returns a pointer to it.  The context can be updated
30 * by calling functions like RpOptimAddParamNumber to define various
31 * input parameters.
32 * ----------------------------------------------------------------------
33 */
34RpOptimEnv*
35RpOptimCreate(pluginData, cleanupPtr)
36    ClientData pluginData;        /* special data created for this env */
37    RpOptimCleanup *cleanupPtr;   /* routine to clean up pluginData */
38{
39    RpOptimEnv *envPtr;
40    envPtr = (RpOptimEnv*)malloc(sizeof(RpOptimEnv));
41    envPtr->pluginData = pluginData;
42    envPtr->cleanupPtr = cleanupPtr;
43
44    envPtr->numParams = 0;
45    envPtr->maxParams = 5;
46    envPtr->paramList = (RpOptimParam**)malloc(
47        (size_t)(envPtr->maxParams*sizeof(RpOptimParam*))
48    );
49
50    return envPtr;
51}
52
53/*
54 * ----------------------------------------------------------------------
55 * RpOptimAddParam()
56 *
57 * Used internally to add a new parameter into the given optimization
58 * context.  The internal list of parameters is resized, if necessary,
59 * to accommodate the new parameter.
60 * ----------------------------------------------------------------------
61 */
62void
63RpOptimAddParam(envPtr, paramPtr)
64    RpOptimEnv *envPtr;      /* context for this optimization */
65    RpOptimParam *paramPtr;  /* parameter being added */
66{
67    RpOptimParam **newParamList;
68
69    /*
70     * Add the new parameter at the end of the list.
71     */
72    envPtr->paramList[envPtr->numParams++] = paramPtr;
73
74    /*
75     * Out of space?  Then double the space available for params.
76     */
77    if (envPtr->numParams >= envPtr->maxParams) {
78        envPtr->maxParams *= 2;
79        newParamList = (RpOptimParam**)malloc(
80            (size_t)(envPtr->maxParams*sizeof(RpOptimParam*))
81        );
82        memcpy(newParamList, envPtr->paramList,
83            (size_t)(envPtr->numParams*sizeof(RpOptimParam*)));
84        free(envPtr->paramList);
85        envPtr->paramList = newParamList;
86    }
87}
88
89/*
90 * ----------------------------------------------------------------------
91 * RpOptimAddParamNumber()
92 *
93 * Used to add a number parameter as an input to an optimization.
94 * Each number has a name and a double precision value that can be
95 * constrained between min/max values.  Adds this number to the end
96 * of the parameter list in the given optimization context.
97 * ----------------------------------------------------------------------
98 */
99RpOptimParam*
100RpOptimAddParamNumber(envPtr, name)
101    RpOptimEnv *envPtr;   /* context for this optimization */
102    char *name;           /* name of this parameter */
103{
104    RpOptimParamNumber *numPtr;
105    numPtr = (RpOptimParamNumber*)malloc(sizeof(RpOptimParamNumber));
106    numPtr->base.name = strdup(name);
107    numPtr->base.type = RP_OPTIMPARAM_NUMBER;
108    numPtr->base.value.num = 0.0;
109    numPtr->min = -DBL_MAX;
110    numPtr->max = DBL_MAX;
111
112    RpOptimAddParam(envPtr, (RpOptimParam*)numPtr);
113
114    return (RpOptimParam*)numPtr;
115}
116
117/*
118 * ----------------------------------------------------------------------
119 * RpOptimAddParamString()
120 *
121 * Used to add a string parameter as an input to an optimization.
122 * Each string has a name and a list of allowed values terminated
123 * by a NULL.  Adds this string to the end of the parameter list
124 * in the given optimization context.
125 * ----------------------------------------------------------------------
126 */
127RpOptimParam*
128RpOptimAddParamString(envPtr, name)
129    RpOptimEnv *envPtr;   /* context for this optimization */
130    char *name;           /* name of this parameter */
131{
132    RpOptimParamString *strPtr;
133    strPtr = (RpOptimParamString*)malloc(sizeof(RpOptimParamString));
134    strPtr->base.name = strdup(name);
135    strPtr->base.type = RP_OPTIMPARAM_STRING;
136    strPtr->base.value.str = NULL;
137    strPtr->values = NULL;
138
139    RpOptimAddParam(envPtr, (RpOptimParam*)strPtr);
140
141    return (RpOptimParam*)strPtr;
142}
143
144/*
145 * ----------------------------------------------------------------------
146 * RpOptimFindParam()
147 *
148 * Used to look for an existing parameter with the specified name.
149 * Returns a pointer to the parameter, or NULL if not found.
150 * ----------------------------------------------------------------------
151 */
152RpOptimParam*
153RpOptimFindParam(envPtr, name)
154    RpOptimEnv *envPtr;   /* context for this optimization */
155    char *name;           /* name of this parameter */
156{
157    int n;
158    for (n=0; envPtr->numParams; n++) {
159        if (strcmp(name, envPtr->paramList[n]->name) == 0) {
160            return envPtr->paramList[n];
161        }
162    }
163    return NULL;
164}
165
166/*
167 * ----------------------------------------------------------------------
168 * RpOptimDeleteParam()
169 *
170 * Used to delete a parameter from the environment.  This is especially
171 * useful when an error is found while configuring the parameter, just
172 * after it was created.  If the name is not recognized, this function
173 * does nothing.
174 * ----------------------------------------------------------------------
175 */
176void
177RpOptimDeleteParam(envPtr, name)
178    RpOptimEnv *envPtr;   /* context for this optimization */
179    char *name;           /* name of this parameter */
180{
181    int n, j;
182    for (n=0; envPtr->numParams; n++) {
183        if (strcmp(name, envPtr->paramList[n]->name) == 0) {
184            RpOptimCleanupParam(envPtr->paramList[n]);
185            for (j=n+1; j < envPtr->numParams; j++) {
186                envPtr->paramList[j-1] = envPtr->paramList[j];
187            }
188            envPtr->numParams--;
189            break;
190        }
191    }
192}
193
194/*
195 * ----------------------------------------------------------------------
196 * RpOptimPerform()
197 *
198 * Used to perform an optimization in the given context.  Each run is
199 * performed by calling an evaluation function represented by a
200 * function pointer.  If an optimum value is found within the limit
201 * on the number of runs, then this procedure returns RP_OPTIM_SUCCESS.
202 * Values for the optimum input parameters are returned through the
203 * paramList within the context.  If the optimization fails, this
204 * function returns RP_OPTIM_FAILURE.
205 * ----------------------------------------------------------------------
206 */
207RpOptimStatus
208RpOptimPerform(envPtr, evalFuncPtr, maxRuns)
209    RpOptimEnv *envPtr;               /* context for this optimization */
210    RpOptimEvaluator *evalFuncPtr;    /* function called to handle run */
211    int maxRuns;                      /* limit on number of runs,
212                                       * or 0 for no limit */
213{
214    RpOptimStatus status = RP_OPTIM_UNKNOWN;
215
216    int n, nruns, ival, nvals;
217    double dval, fitness;
218    RpOptimParamNumber *numPtr;
219    RpOptimParamString *strPtr;
220    RpOptimStatus runStatus;
221
222    if (envPtr->numParams == 0) {    /* no input parameters? */
223        return RP_OPTIM_FAILURE;     /* then we can't optimize! */
224    }
225
226    /*
227     * Call the evaluation function a number of times with different
228     * values and perform the optimization.
229     */
230    nruns = 0;
231    while (status == RP_OPTIM_UNKNOWN) {
232        /*
233         * Pick random values for all inputs.
234         */
235        for (n=0; n < envPtr->numParams; n++) {
236            switch (envPtr->paramList[n]->type) {
237                case RP_OPTIMPARAM_NUMBER:
238                    numPtr = (RpOptimParamNumber*)envPtr->paramList[n];
239                    dval = drand48();
240                    envPtr->paramList[n]->value.num =
241                        (numPtr->max - numPtr->min)*dval + numPtr->min;
242                    break;
243                case RP_OPTIMPARAM_STRING:
244                    strPtr = (RpOptimParamString*)envPtr->paramList[n];
245                    for (nvals=0; strPtr->values[nvals]; nvals++)
246                        ;  /* count values */
247                    ival = (int)floor(drand48() * nvals);
248                    envPtr->paramList[n]->value.str = strPtr->values[ival];
249                    break;
250            }
251        }
252
253        /*
254         * Call the evaluation function to get the fitness value.
255         */
256        runStatus = (*evalFuncPtr)(envPtr->paramList, envPtr->numParams,
257            &fitness);
258
259        if (runStatus == RP_OPTIM_SUCCESS) {
260            /*
261             * Is the fitness function any better?
262             * Change the input values here based on fitness.
263             *  ...
264             */
265        }
266
267        if (++nruns >= maxRuns && maxRuns > 0) {
268            status = RP_OPTIM_FAILURE;  /* reached the limit of runs */
269        }
270    }
271
272    return status;
273}
274
275/*
276 * ----------------------------------------------------------------------
277 * RpOptimDelete()
278 *
279 * Used to delete the context for an optimization once it is finished
280 * or no longer needed.  Frees up the memory needed to store the
281 * context and all input parameters.  After this call, the context
282 * should not be used again.
283 * ----------------------------------------------------------------------
284 */
285void
286RpOptimDelete(envPtr)
287    RpOptimEnv *envPtr;   /* context for this optimization */
288{
289    int n;
290
291    for (n=0; n < envPtr->numParams; n++) {
292        RpOptimCleanupParam(envPtr->paramList[n]);
293    }
294    if (envPtr->cleanupPtr) {
295        (*envPtr->cleanupPtr)(envPtr->pluginData);
296    }
297    free(envPtr->paramList);
298    free(envPtr);
299}
300
301/*
302 * ----------------------------------------------------------------------
303 * RpOptimCleanupParam()
304 *
305 * Used internally to free up the data associated with an optimization
306 * parameter.  Looks at the parameter type and frees up the appropriate
307 * data.
308 * ----------------------------------------------------------------------
309 */
310static void
311RpOptimCleanupParam(paramPtr)
312    RpOptimParam *paramPtr;  /* data to be freed */
313{
314    int n;
315    RpOptimParamNumber *numPtr;
316    RpOptimParamString *strPtr;
317
318    free(paramPtr->name);
319    switch (paramPtr->type) {
320        case RP_OPTIMPARAM_NUMBER:
321            numPtr = (RpOptimParamNumber*)paramPtr;
322            /* nothing special to free here */
323            break;
324        case RP_OPTIMPARAM_STRING:
325            strPtr = (RpOptimParamString*)paramPtr;
326            if (strPtr->values) {
327                for (n=0; strPtr->values[n]; n++) {
328                    free(strPtr->values[n]);
329                }
330            }
331            break;
332    }
333    free(paramPtr);
334}
Note: See TracBrowser for help on using the repository browser.