209 {
210 int64_t i;
212 SCANNED_ARG *s_arg;
213 long i_arg, ii;
214 char *input, *output, *xName, *yName, *syName, **colMatch;
215 long fitIndex, residualIndex, retval;
216 int32_t colMatches;
217 double rmsResidual, chiSqr, sigLevel;
218 unsigned long pipeFlags, dummyFlags, majorOrderFlag;
219 int32_t nEvalMax = 5000, nPassMax = 10, nRestartMax = 10;
220 long nEval, iRestart;
221 double tolerance, target;
222 char **variableUnits;
223 double *lowerLimit, *upperLimit, *stepSize, *startingValue, *paramValue, *paramDelta = NULL, *paramDelta0 = NULL;
224 char **startingPar = NULL, **lowerLimitPar = NULL, **upperLimitPar = NULL, **heatPar = NULL, **stepPar = NULL;
225 double *heat, *bestParamValue, bestResult = 0;
226 long iVariable, startFromPrevious = 0;
227 double result, lastResult = 0;
228 char pfix[IFPF_BUF_SIZE];
229 unsigned long simplexFlags = 0;
230 char *ptr;
231 short columnMajorOrder = -1;
232
233 signal(SIGINT, optimizationInterruptHandler);
234
236 argc =
scanargs(&s_arg, argc, argv);
237 colMatches = 0;
238 colMatch = NULL;
239 if (argc <= 1) {
240 fprintf(stderr, "%s", USAGE);
241 exit(EXIT_FAILURE);
242 }
243 logFile = NULL;
244 input = output = equation = NULL;
245 variableName = variableUnits = NULL;
246 lowerLimit = upperLimit = stepSize = startingValue = heat = bestParamValue = NULL;
247 nVariables = 0;
248 pipeFlags = 0;
249 verbosity = startFromPrevious = 0;
250 xName = yName = syName = NULL;
251 tolerance = target = 1e-14;
252
253 for (i_arg = 1; i_arg < argc; i_arg++) {
254 if (s_arg[i_arg].arg_type == OPTION) {
255 switch (
match_string(s_arg[i_arg].list[0], option, N_OPTIONS, 0)) {
256 case SET_MAJOR_ORDER:
257 majorOrderFlag = 0;
258 s_arg[i_arg].n_items--;
259 if (s_arg[i_arg].n_items > 0 && (!
scanItemList(&majorOrderFlag, s_arg[i_arg].list + 1, &s_arg[i_arg].n_items, 0,
"row", -1, NULL, 0, SDDS_ROW_MAJOR_ORDER,
"column", -1, NULL, 0, SDDS_COLUMN_MAJOR_ORDER, NULL)))
260 SDDS_Bomb(
"Invalid -majorOrder syntax/values");
261 if (majorOrderFlag & SDDS_COLUMN_MAJOR_ORDER)
262 columnMajorOrder = 1;
263 else if (majorOrderFlag & SDDS_ROW_MAJOR_ORDER)
264 columnMajorOrder = 0;
265 break;
266 case SET_TOLERANCE:
267 if (s_arg[i_arg].n_items != 2 || sscanf(s_arg[i_arg].list[1], "%lf", &tolerance) != 1 || tolerance <= 0)
268 SDDS_Bomb(
"Incorrect -tolerance syntax");
269 break;
270 case SET_TARGET:
271 if (s_arg[i_arg].n_items != 2 || sscanf(s_arg[i_arg].list[1], "%lf", &target) != 1 || target <= 0)
273 break;
274 case SET_VERBOSITY:
275 if (s_arg[i_arg].n_items != 2 || sscanf(s_arg[i_arg].list[1], "%ld", &verbosity) != 1)
276 SDDS_Bomb(
"Incorrect -verbosity syntax");
277 break;
278 case SET_COLUMNS:
279 if (s_arg[i_arg].n_items != 3 && s_arg[i_arg].n_items != 4)
281 xName = s_arg[i_arg].list[1];
282 yName = s_arg[i_arg].list[2];
283 s_arg[i_arg].n_items -= 3;
284 if (!
scanItemList(&dummyFlags, s_arg[i_arg].list + 3, &s_arg[i_arg].n_items, 0,
"ysigma",
SDDS_STRING, &syName, 1, 0, NULL))
286 break;
287 case SET_YCOLUMN:
288 if (s_arg[i_arg].n_items != 2 && s_arg[i_arg].n_items != 3)
290 yName = s_arg[i_arg].list[1];
291 s_arg[i_arg].n_items -= 2;
292 if (!
scanItemList(&dummyFlags, s_arg[i_arg].list + 2, &s_arg[i_arg].n_items, 0,
"ysigma",
SDDS_STRING, &syName, 1, 0, NULL))
294 break;
295 case SET_COPYCOLUMNS:
296 if (s_arg[i_arg].n_items < 2)
297 SDDS_Bomb(
"Invalid -copycolumns syntax provided.");
298 colMatch =
tmalloc(
sizeof(*colMatch) * (colMatches = s_arg[i_arg].n_items - 1));
299 for (i = 0; i < colMatches; i++)
300 colMatch[i] = s_arg[i_arg].list[i + 1];
301 break;
302 case SET_PIPE:
303 if (!
processPipeOption(s_arg[i_arg].list + 1, s_arg[i_arg].n_items - 1, &pipeFlags))
305 break;
306 case SET_LOGFILE:
307 if (s_arg[i_arg].n_items != 2 && s_arg[i_arg].n_items != 3)
309 logFile = s_arg[i_arg].list[1];
310 if (s_arg[i_arg].n_items == 3 && (sscanf(s_arg[i_arg].list[2], "%" SCNd32, &maxLogRows) != 1 || maxLogRows <= 0))
312 break;
313 case SET_VARIABLE:
314 if (!(variableName =
SDDS_Realloc(variableName,
sizeof(*variableName) * (nVariables + 1))) ||
315 !(lowerLimit =
SDDS_Realloc(lowerLimit,
sizeof(*lowerLimit) * (nVariables + 1))) ||
316 !(upperLimit =
SDDS_Realloc(upperLimit,
sizeof(*upperLimit) * (nVariables + 1))) ||
317 !(stepSize =
SDDS_Realloc(stepSize,
sizeof(*stepSize) * (nVariables + 1))) ||
318 !(heat =
SDDS_Realloc(heat,
sizeof(*heat) * (nVariables + 1))) ||
319 !(bestParamValue =
SDDS_Realloc(bestParamValue,
sizeof(*bestParamValue) * (nVariables + 1))) ||
320 !(startingValue =
SDDS_Realloc(startingValue,
sizeof(*startingValue) * (nVariables + 1))) ||
321 !(startingPar =
SDDS_Realloc(startingPar,
sizeof(*startingPar) * (nVariables + 1))) ||
322 !(lowerLimitPar =
SDDS_Realloc(lowerLimitPar,
sizeof(*lowerLimitPar) * (nVariables + 1))) ||
323 !(upperLimitPar =
SDDS_Realloc(upperLimitPar,
sizeof(*upperLimitPar) * (nVariables + 1))) ||
324 !(heatPar =
SDDS_Realloc(heatPar,
sizeof(*heatPar) * (nVariables + 1))) ||
325 !(stepPar =
SDDS_Realloc(stepPar,
sizeof(*stepPar) * (nVariables + 1))) ||
326 !(variableUnits =
SDDS_Realloc(variableUnits,
sizeof(*variableUnits) * (nVariables + 1))))
328 variableUnits[nVariables] = NULL;
329 heat[nVariables] = 0;
330 startingPar[nVariables] = heatPar[nVariables] = lowerLimitPar[nVariables] = upperLimitPar[nVariables] = stepPar[nVariables] = NULL;
331 if ((s_arg[i_arg].n_items -= 1) < 5 ||
332 !
scanItemList(&dummyFlags, s_arg[i_arg].list + 1, &s_arg[i_arg].n_items, 0,
333 "name",
SDDS_STRING, variableName + nVariables, 1, VARNAME_GIVEN,
334 "lowerlimit",
SDDS_STRING, lowerLimitPar + nVariables, 1, LOWER_GIVEN,
335 "upperlimit",
SDDS_STRING, upperLimitPar + nVariables, 1, UPPER_GIVEN,
336 "stepsize",
SDDS_STRING, &(stepPar[nVariables]), 1, STEP_GIVEN,
337 "startingvalue",
SDDS_STRING, startingPar + nVariables, 1, START_GIVEN,
339 "units",
SDDS_STRING, variableUnits + nVariables, 1, VARUNITS_GIVEN, NULL))
340 SDDS_Bomb(
"Invalid -variable syntax or values");
341 if (startingPar[nVariables] && sscanf(startingPar[nVariables], "%lf", startingValue + nVariables) == 1) {
342 free(startingPar[nVariables]);
343 startingPar[nVariables] = NULL;
344 }
345 if (lowerLimitPar[nVariables] && sscanf(lowerLimitPar[nVariables], "%lf", lowerLimit + nVariables) == 1) {
346 free(lowerLimitPar[nVariables]);
347 lowerLimitPar[nVariables] = NULL;
348 }
349 if (upperLimitPar[nVariables] && sscanf(upperLimitPar[nVariables], "%lf", upperLimit + nVariables) == 1) {
350 free(upperLimitPar[nVariables]);
351 upperLimitPar[nVariables] = NULL;
352 }
353 if (heatPar[nVariables] && sscanf(heatPar[nVariables], "%lf", heat + nVariables) == 1) {
354 free(heatPar[nVariables]);
355 heatPar[nVariables] = NULL;
356 }
357 if (stepPar[nVariables] && sscanf(stepPar[nVariables], "%lf", stepSize + nVariables) == 1) {
358 free(stepPar[nVariables]);
359 stepPar[nVariables] = NULL;
360 }
361 if ((dummyFlags & (VARNAME_GIVEN | LOWER_GIVEN | UPPER_GIVEN | STEP_GIVEN | START_GIVEN)) != (VARNAME_GIVEN | LOWER_GIVEN | UPPER_GIVEN | STEP_GIVEN | START_GIVEN))
362 SDDS_Bomb(
"Insufficient information given for -variable");
363 if (!strlen(variableName[nVariables]))
364 SDDS_Bomb(
"Invalid blank variable name");
365 if (!lowerLimitPar[nVariables] && !upperLimitPar[nVariables] && lowerLimit[nVariables] >= upperLimit[nVariables])
366 SDDS_Bomb(
"Invalid limits value for variable");
367
368 if (!lowerLimitPar[nVariables] && !upperLimitPar[nVariables] && !startingPar[nVariables] &&
369 (startingValue[nVariables] <= lowerLimit[nVariables] || startingValue[nVariables] >= upperLimit[nVariables]))
370 SDDS_Bomb(
"Invalid limits or starting value for variable");
371 if (!stepPar[nVariables] && stepSize[nVariables] <= 0)
372 SDDS_Bomb(
"Invalid step size for variable");
373 nVariables++;
374 break;
375 case SET_SIMPLEX:
376 s_arg[i_arg].n_items -= 1;
377 if (!
scanItemList(&simplexFlags, s_arg[i_arg].list + 1, &s_arg[i_arg].n_items, 0,
378 "restarts",
SDDS_LONG, &nRestartMax, 1, 0,
380 "evaluations",
SDDS_LONG, &nEvalMax, 1, 0,
381 "no1dscans", -1, NULL, 0, SIMPLEX_NO_1D_SCANS, NULL) ||
382 nRestartMax < 0 || nPassMax <= 0 || nEvalMax <= 0)
383 SDDS_Bomb(
"Invalid -simplex syntax/values");
384 break;
385 case SET_EQUATION:
386 if ((s_arg[i_arg].n_items < 2) || (s_arg[i_arg].n_items > 3))
388 if (s_arg[i_arg].n_items == 2) {
389 if (!strlen(equation = s_arg[i_arg].list[1])) {
391 }
392 } else if (s_arg[i_arg].n_items == 3) {
393 if (strncmp(s_arg[i_arg].list[2], "algebraic", strlen(s_arg[i_arg].list[2])) == 0) {
394 ptr = addOuterParentheses(s_arg[i_arg].list[1]);
395 if2pf(pfix, ptr, sizeof pfix);
396 free(ptr);
398 fprintf(stderr, "Error: Problem copying equation string\n");
399 exit(EXIT_FAILURE);
400 }
401 } else {
403 }
404 }
405 break;
406 case SET_EXPRESSION:
407 if (s_arg[i_arg].n_items != 2 && s_arg[i_arg].n_items != 3)
409 expression =
trealloc(expression,
sizeof(*expression) * (nExpressions + 1));
410 expression[nExpressions] = s_arg[i_arg].list[1];
411 expressionColumn =
trealloc(expressionColumn,
sizeof(*expressionColumn) * (nExpressions + 1));
412 if (s_arg[i_arg].n_items == 3)
413 expressionColumn[nExpressions] = s_arg[i_arg].list[2];
414 else
415 expressionColumn[nExpressions] = NULL;
416 nExpressions++;
417 break;
418 case SET_STARTFROMPREVIOUS:
419 startFromPrevious = 1;
420 break;
421 default:
422 fprintf(stderr, "Error: Unknown or ambiguous option: %s\n", s_arg[i_arg].list[0]);
423 exit(EXIT_FAILURE);
424 break;
425 }
426 } else {
427 if (input == NULL)
428 input = s_arg[i_arg].list[0];
429 else if (output == NULL)
430 output = s_arg[i_arg].list[0];
431 else
432 SDDS_Bomb(
"Too many filenames provided");
433 }
434 }
435
437
438 if (!yName)
439 SDDS_Bomb(
"-ycolumn option must be given");
440 if (nVariables == 0)
441 SDDS_Bomb(
"You must specify at least one -variable option");
442 if (equation == NULL)
443 SDDS_Bomb(
"You must specify an equation string");
444
445 rpn(getenv("RPN_DEFNS"));
446 if (rpn_check_error())
447 exit(EXIT_FAILURE);
448
451 if ((xName && !
SDDS_FindColumn(&InputTable, FIND_NUMERIC_TYPE, xName, NULL)) ||
453 (syName && !
SDDS_FindColumn(&InputTable, FIND_NUMERIC_TYPE, syName, NULL)))
454 SDDS_Bomb(
"One or more of the specified data columns does not exist or is non-numeric");
455
456 setupOutputFile(&OutputTable, &fitIndex, &residualIndex, output, &InputTable, xName, yName, syName,
457 variableName, variableUnits, nVariables, colMatch, colMatches,
458 expression, expressionColumn, nExpressions,
459 &logData, logFile, columnMajorOrder);
460
461 if (!(paramValue =
SDDS_Malloc(
sizeof(*paramValue) * nVariables)) ||
462 !(paramDelta =
SDDS_Malloc(
sizeof(*paramDelta) * nVariables)) ||
463 !(paramDelta0 =
SDDS_Malloc(
sizeof(*paramDelta0) * nVariables)) ||
464 !(variableMem =
SDDS_Malloc(
sizeof(*variableMem) * nVariables)))
466 for (iVariable = 0; iVariable < nVariables; iVariable++)
467 variableMem[iVariable] = rpn_create_mem(variableName[iVariable], 0);
468
475
477 continue;
478
479 for (iVariable = 0; iVariable < nVariables; iVariable++) {
480 if (startingPar[iVariable] && !
SDDS_GetParameterAsDouble(&InputTable, startingPar[iVariable], &startingValue[iVariable]))
482 if (lowerLimitPar[iVariable] && !
SDDS_GetParameterAsDouble(&InputTable, lowerLimitPar[iVariable], &lowerLimit[iVariable]))
484 if (upperLimitPar[iVariable] && !
SDDS_GetParameterAsDouble(&InputTable, upperLimitPar[iVariable], &upperLimit[iVariable]))
493 } else if (retval == 1 || !startFromPrevious)
494 paramValue[iVariable] = startingValue[iVariable];
495 paramDelta[iVariable] = stepSize[iVariable];
496 }
497 if (verbosity > 2) {
498
499 fprintf(stderr, "Starting values and step sizes:\n");
500 for (iVariable = 0; iVariable < nVariables; iVariable++)
501 fprintf(stderr, " %s = %le %le\n", variableName[iVariable], paramValue[iVariable], paramDelta[iVariable]);
502 }
503
504 if (!(yFit =
SDDS_Realloc(yFit,
sizeof(*yFit) * nData)) ||
505 !(yResidual =
SDDS_Realloc(yResidual,
sizeof(*yResidual) * nData)))
507 if (nExpressions) {
508 long j;
509 expressionValue =
SDDS_Realloc(expressionValue,
sizeof(*expressionValue) * nExpressions);
510 for (j = 0; j < nExpressions; j++)
511 expressionValue[j] = malloc(sizeof(**expressionValue) * nData);
512 }
513 SDDS_StoreParametersInRpnMemories(&InputTable);
514 memcpy(paramDelta0, paramDelta, sizeof(*paramDelta) * nVariables);
515 for (iRestart = nEval = 0; iRestart <= nRestartMax; iRestart++) {
516 memcpy(paramDelta, paramDelta0, sizeof(*paramDelta) * nVariables);
517 if (iRestart) {
518 if (iRestart == 1)
519 random_1(-labs(2 * ((
long)time(NULL) / 2) + 1));
520 for (iVariable = 0; iVariable < nVariables; iVariable++) {
522 if (paramValue[iVariable] < lowerLimit[iVariable])
523 paramValue[iVariable] = lowerLimit[iVariable] + paramDelta[iVariable];
524 if (paramValue[iVariable] > upperLimit[iVariable])
525 paramValue[iVariable] = upperLimit[iVariable] - paramDelta[iVariable];
526 }
527 }
528 nEval +=
simplexMin(&result, paramValue, paramDelta, lowerLimit, upperLimit, NULL, nVariables, target, tolerance, fitFunction, (verbosity > 0 ? report : NULL), nEvalMax, nPassMax, 12, 3, 1.0, simplexFlags);
529 if (iRestart != 0 && result > bestResult) {
530 result = bestResult;
531 for (iVariable = 0; iVariable < nVariables; iVariable++)
532 paramValue[iVariable] = bestParamValue[iVariable];
533 } else {
534 bestResult = result;
535 for (iVariable = 0; iVariable < nVariables; iVariable++)
536 bestParamValue[iVariable] = paramValue[iVariable];
537 }
538 if (verbosity > 0)
539 fprintf(stderr, "Result of simplex minimization: %le\n", result);
540 if (result < target || (iRestart != 0 && fabs(lastResult - result) < tolerance))
541 break;
542 lastResult = result;
543 if (abortRequested)
544 break;
545 if (verbosity > 0)
546 fprintf(stderr, "Performing restart %ld\n", iRestart + 1);
547 }
548
549 for (iVariable = 0; iVariable < nVariables; iVariable++)
550 paramValue[iVariable] = bestParamValue[iVariable];
551 result = fitFunction(paramValue, &ii);
552
553 if (verbosity > 3)
554 fprintf(stderr, "%ld evaluations of fit function required, giving result %e\n", nEval, result);
555 for (i = result = 0; i < nData; i++)
556 result += sqr(yResidual[i]);
557 rmsResidual = sqrt(result / nData);
558 if (syData) {
559 for (i = chiSqr = 0; i < nData; i++)
560 chiSqr += sqr(yResidual[i] / syData[i]);
561 } else {
562 double sy2;
563 sy2 = result / (nData - nVariables);
564 for (i = chiSqr = 0; i < nData; i++)
565 chiSqr += sqr(yResidual[i]) / sy2;
566 }
568 if (verbosity > 1) {
569 if (syData)
570 fprintf(stderr, "Significance level: %.5e\n", sigLevel);
571 fprintf(stderr, "RMS deviation: %.15e\n", rmsResidual);
572 }
573
576 !
SDDS_SetColumn(&OutputTable, SDDS_SET_BY_INDEX, yFit, nData, fitIndex) ||
577 !
SDDS_SetColumn(&OutputTable, SDDS_SET_BY_INDEX, yResidual, nData, residualIndex))
579 for (i = 0; i < nExpressions; i++)
580 if (expressionColumn[i] &&
581 !
SDDS_SetColumn(&OutputTable, SDDS_SET_BY_NAME, expressionValue[i], nData, expressionColumn[i]))
583 for (iVariable = 0; iVariable < nVariables; iVariable++)
584 if (!
SDDS_SetParameters(&OutputTable, SDDS_PASS_BY_VALUE | SDDS_SET_BY_NAME, variableName[iVariable], paramValue[iVariable], NULL))
590 step = 0;
591 free(xData);
592 free(yData);
593 if (syData)
594 free(syData);
595 }
600 if (colMatches)
601 free(colMatch);
602 if (syName)
603 free(syName);
606 free(lowerLimit);
607 free(upperLimit);
608 free(stepSize);
609 free(heat);
610 free(bestParamValue);
611 free(startingValue);
612 free(startingPar);
613 free(lowerLimitPar);
614 free(upperLimitPar);
615 free(heatPar);
616 free(stepPar);
617 free(variableUnits);
618
619 return EXIT_SUCCESS;
620}
int32_t SDDS_CopyPage(SDDS_DATASET *SDDS_target, SDDS_DATASET *SDDS_source)
int32_t SDDS_SetParameters(SDDS_DATASET *SDDS_dataset, int32_t mode,...)
int32_t SDDS_SetColumn(SDDS_DATASET *SDDS_dataset, int32_t mode, void *data, int64_t rows,...)
Sets the values for one data column in the current data table of an SDDS dataset.
int32_t SDDS_WritePage(SDDS_DATASET *SDDS_dataset)
Writes the current data table to the output file.
int32_t SDDS_FreeStringArray(char **string, int64_t strings)
Frees an array of strings by deallocating each individual string.
int32_t SDDS_GetParameterIndex(SDDS_DATASET *SDDS_dataset, char *name)
Retrieves the index of a named parameter in the SDDS dataset.
void * SDDS_Malloc(size_t size)
Allocates memory of a specified size.
void SDDS_RegisterProgramName(const char *name)
Registers the executable program name for use in error messages.
char * SDDS_FindColumn(SDDS_DATASET *SDDS_dataset, int32_t mode,...)
Finds the first column in the SDDS dataset that matches the specified criteria.
int32_t SDDS_CopyString(char **target, const char *source)
Copies a source string to a target string with memory allocation.
void * SDDS_Realloc(void *old_ptr, size_t new_size)
Reallocates memory to a new size.
#define SDDS_STRING
Identifier for the string data type.
#define SDDS_LONG
Identifier for the signed 32-bit integer data type.
void * trealloc(void *old_ptr, uint64_t size_of_block)
Reallocates a memory block to a new size.
void * tmalloc(uint64_t size_of_block)
Allocates a memory block of the specified size with zero initialization.
double random_1(long iseed)
Generate a uniform random double in [0,1] using a custom seed initialization.
double gauss_rn_lim(double mean, double sigma, double limit_in_sigmas, double(*urandom)(long iseed))
Generate a Gaussian-distributed random number with specified mean, sigma, and optional cutoff.
long match_string(char *string, char **option, long n_options, long mode)
Matches a given string against an array of option strings based on specified modes.
int scanargs(SCANNED_ARG **scanned, int argc, char **argv)
long processPipeOption(char **item, long items, unsigned long *flags)
void processFilenames(char *programName, char **input, char **output, unsigned long pipeFlags, long noWarnings, long *tmpOutputUsed)
void free_scanargs(SCANNED_ARG **scanned, int argc)
long scanItemList(unsigned long *flags, char **item, long *items, unsigned long mode,...)
Scans a list of items and assigns values based on provided keywords and types.
double ChiSqrSigLevel(double ChiSquared0, long nu)
Computes the probability that a chi-squared variable exceeds a given value.
long simplexMin(double *yReturn, double *xGuess, double *dxGuess, double *xLowerLimit, double *xUpperLimit, short *disable, long dimensions, double target, double tolerance, double(*func)(double *x, long *invalid), void(*report)(double ymin, double *xmin, long pass, long evals, long dims), long maxEvaluations, long maxPasses, long maxDivisions, double divisorFactor, double passRangeFactor, unsigned long flags)
Top-level convenience function for simplex-based minimization.