[mod.sources] Simplex Curve Fitting Algorithm in C

sources-request@panda.UUCP (02/23/86)

Mod.sources:  Volume 4, Issue 2
Submitted by: panda!genrad!decvax!ihnp4!chinet!blm

This program originally appeared in the May 1984 issue of Byte Magazine.  It
was originally written in Pascal by Marco Caceci and William Caceris at Florida
State University.  I have translated it to 'C'.

This program is based upon the Simplex curve fitting algorithm.  For a detailed
descripstion of this program and it's workings see the above mentioned article.

I acknowledge the work of Marco Caceci and William Caceris for writing the
original Pascal program from which this is derived.  The original authors
explicitly stated ''no copy-right''.

I've had some problems with accuracy.  I've checked and rechecked my source
code and I can't find anything that would account for it.  I could very well be
overlooking something, though.  I suppose it could have to do with differences
in the precision of the floating-point libraries of the authors Pascal compiler
(Pascal/Z Version 4.0 CP/M) and my C compiler (Xenix 3.0).  I welcome E-mail
on this subject.  Nevertheless, the differences in values returned between
the original and mine are comparatively small.

I hope this helps someone in Netland.
-----
The preceding announcement was conceived in my own mind and is not
necessarily the opinion of whom ever I happen to work for at the time.

Name  : Brad L. McKinley --- (503) 889-4321
USMail: M D R Professional Software, Inc., 915 SW 3rd Avenue, Ontario, OR 97914
Usenet: ihnp4!chinet!blm OR ihnp4!chinet!mdr!blm
CIS   : 70015,1205
Source: BDY171
"God created Arrakis to test the faithful." -- Maud'dib

--- cut here ------ cut here ------ cut here ------ cut here ---
: This is a shar archive.  Extract with sh, not csh.
: The rest of this file will extract:
:      Makefile
:      data              sample data file
:      enter.c
:      f.c               a typical function
:      first.c
:      main.c
:      new_vertex.c
:      order.c
:      report.c
:      sum_residual.c
:
echo extracting -- Makefile
sed 's/^X//' > Makefile << E_O_F
XBINARY = simplex
X
XSOURCES = main.c f.c sum_residual.c enter.c first.c new_vertex.c order.c report.c
X
XOBJECTS = main.o f.o sum_residual.o enter.o first.o new_vertex.o order.o report.o
X
XLIBRARIES = -lm -lc
X
XCFLAGS = -O
XLDFLAGS = -n -s -x
XLINTFLAGS =
X
X$(BINARY): $(OBJECTS)
X	@echo "	loading $(BINARY)"
X	@ld -o $@ $(LDFLAGS) /lib/crt0.o $(OBJECTS) $(LIBRARIES)
X
Xlint:
X	lint $(LINTFLAGS) $(SOURCES)
X
X$(OBJECTS): simplex.h
E_O_F

echo extracting -- data
sed 's/^X//' > data << E_O_F
X100
X0.2 3
X0.1 1
X1e-6 1e-6 1e-6
X1.68 0.172
X3.33 0.250
X5.00 0.286
X6.67 0.303
X10.0 0.334
X20.0 0.384
E_O_F

echo extracting -- simplex.h
sed 's/^X//' > simplex.h << E_O_F
X#include <math.h>
X#include <stdio.h>
X
X#define M      2
X#define NVPP   2
X#define N      M+1
X#define MNP    200
X#define ALPHA  1.0
X#define BETA   0.5
X#define GAMMA  2.0
X#define LW     5
X#define ROOT2  1.414214
X
Xtypedef double vector[N];
Xtypedef double datarow[NVPP];
X
Xextern int       h[N], l[N];
Xextern int       np, maxiter, niter;
Xextern vector    next, mean, error, maxerr, step, simp[N];
Xextern datarow   data[MNP];
Xextern FILE      *fpdata;
X
Xextern double    f();
E_O_F

echo extracting -- enter.c
sed 's/^X//' > enter.c << E_O_F
X#include "simplex.h"
X
Xenter(fname)
Xchar *fname;
X{
X     register int   i, j;
X
X     printf("SIMPLEX Optimization --- 'C' Version\n\n");
X     printf("Accessing file: %s\n\n", fname);
X
X     fscanf(fpdata, "%d", &maxiter);
X     printf("maximum number of iterations = %d\n\n", maxiter);
X
X     printf("Start Coordinates: ");
X     for (i=0 ; i<M ; i++) {
X          fscanf(fpdata, "%F", &simp[0][i]);
X          if ((i+1) % LW == 0)
X               printf("\n");
X          printf(" %e", simp[0][i]);
X     }
X     printf("\n\n");
X
X     printf("Start Steps: ");
X     for (i=0 ; i<M ; i++) {
X          fscanf(fpdata, "%F", &step[i]);
X          if ((i+1) % LW == 0)
X               printf("\n");
X          printf(" %e", step[i]);
X     }
X     printf("\n\n");
X
X     printf("Maximum Error: ");
X     for (i=0 ; i<N ; i++) {
X          fscanf(fpdata, "%F", &maxerr[i]);
X          if ((i+1) % LW == 0)
X               printf("\n");
X          printf(" %e", maxerr[i]);
X     }
X     printf("\n\n");
X
X     printf("DATA\n");
X     printf("      X             Y\n");
X     np = 0;
X     while (!feof(fpdata)) {
X          for (j=0 ; j<NVPP ; j++) {
X               if (fscanf(fpdata, "%F", &data[np][j]) == EOF)
X                    break;
X               printf("  %e", data[np][j]);
X          }
X          np++;
X          printf("\n");
X     }
X     np--;
X
X}
E_O_F

echo extracting -- f.c
sed 's/^X//' > f.c << E_O_F
X#include "simplex.h"
X
Xdouble f(x, d)
Xvector    x;
Xdatarow   d;
X{
X     return (x[0]*d[0]/(x[1]+d[0]));
X}
E_O_F

echo extracting -- first.c
sed 's/^X//' > first.c << E_O_F
X#include "simplex.h"
X
Xfirst()
X{
X     register int   i, j;
X
X     printf("Starting Simplex\n");
X     for (j=0 ; j<N ; j++) {
X          printf(" simp[%d]", j+1);
X          for (i=0 ; i<N ; i++) {
X               if ((i+1) % LW == 0)
X                    printf("\n");
X               printf("  %e", simp[j][i]);
X          }
X          printf("\n");
X     }
X     printf("\n");
X}
E_O_F

echo extracting -- main.c
sed 's/^X//' > main.c << E_O_F
X#include "simplex.h"
X
X#define until(x) while (!(x))
X
Xint       h[N], l[N];
Xint       np, maxiter, niter;
Xvector    next, mean, error, maxerr, step, simp[N];
Xdatarow   data[MNP];
XFILE      *fpdata;
X
Xmain(argc, argv)
Xint   argc;
Xchar  *argv[];
X{
X   register int   i, j, done;
X   vector         center, p, q;
X
X   if (argc != 2) {
X      fprintf(stderr, "usage: simplex file_name\n", argv[1]);
X      exit(1);
X   }
X
X   if ((fpdata = fopen(argv[1], "r")) == NULL) {
X      fprintf(stderr, "simplex: can't open %s\n", argv[1]);
X      exit(1);
X   }
X
X   enter(argv[1]);
X
X   /* First Vertex */
X   sum_residual(simp[0]);
X
X   /* Compute offset of Vertices */
X   for (i=0 ; i<M ; i++) {
X      p[i] = step[i] * (sqrt((double) N) + M - 1) / (M * ROOT2);
X      q[i] = step[i] * (sqrt((double) N) - 1) / (M * ROOT2);
X   }
X
X   /* All Vertices of the Starting Simplex */
X   for (i=1 ; i<N ; i++) {
X      for (j=0 ; j<M ; j++)
X         simp[i][j] = simp[0][j] + q[j];
X      simp[i][i-1] = simp[0][i-1] + p[i-1];
X      sum_residual(simp[i]);
X   }
X
X   /* Preset */
X   for (i=0 ; i<N ; i++) {
X      l[i] = 1;
X      h[i] = 1;
X   }
X   order();
X
X   first();
X
X   niter = 0;
X
X   /* Iterate */
X   do {
X      /* Wish it were True */
X      done = 1;
X      niter++;
X
X      /* Compute Centroid...Excluding the Worst */
X      for (i=0 ; i<N ; i++)
X         center[i] = 0.0;
X      for (i=0 ; i<N ; i++)
X         if (i != h[N-1])
X            for (j=0 ; j<M ; j++)
X               center[j] += simp[i][j];
X
X      /* First Attempt to Reflect */
X      for (i=0 ; i<N ; i++) {
X         center[i] /= M;
X         next[i] = (1.0+ALPHA) * center[i] - ALPHA * simp[h[N-1]][i];
X      }
X      sum_residuals(next);
X
X      if (next[N-1] <= simp[l[N-1]][N-1]) {
X         new_vertex();
X         for (i=0 ; i<M ; i++)
X            next[i] = GAMMA * simp[h[N-1]][i] + (1.0-GAMMA) * center[i];
X         sum_residual(next);
X         if (next[N-1] <= simp[l[N-1]][N-1])
X            new_vertex();
X      }
X      else {
X         if (next[N-1] <= simp[h[N-1]][N-1])
X            new_vertex();
X         else {
X            for (i=0 ; i<M ; i++)
X               next[i] = BETA * simp[h[N-1]][i] + (1.0-BETA) * center[i];
X            sum_residual(next);
X            if (next[N-1] <= simp[h[N-1]][N-1])
X               new_vertex();
X            else {
X               for (i=0 ; i<N ; i++) {
X                  for (j=0 ; j<M ; j++)
X                     simp[i][j] = BETA * (simp[i][j] + simp[l[N-1]][j]);
X                  sum_residual(simp[i]);
X               }
X            }
X         }
X      }
X
X      order();
X
X      /* Check For Convergence */
X      for (j=0 ; j<N ; j++) {
X         error[j] = (simp[h[j]][j] - simp[l[j]][j]) / simp[h[j]][j];
X         if (done)
X            if (error[j] > maxerr[j])
X               done = 0;
X      }
X
X   } until(done || (niter == maxiter));
X
X   /* Average Each Parameter */
X   for (i=0 ; i<N ; i++) {
X      mean[i] = 0.0;
X      for (j=0 ; j<N ; j++)
X         mean[i] += simp[j][i];
X      mean[i] /= N;
X   }
X
X   report();
X}
E_O_F

echo extracting -- new_vertex.c
sed 's/^X//' > new_vertex.c << E_O_F
X#include "simplex.h"
X
Xnew_vertex()
X{
X     register int   i;
X
X     printf(" --- %4d", niter);
X     for (i=0 ; i<N ; i++) {
X          simp[h[N-1]][i] = next[i];
X          printf("  %e", next[i]);
X     }
X     printf("\n");
X}
E_O_F

echo extracting -- order.c
sed 's/^X//' > order.c << E_O_F
X#include "simplex.h"
X
Xorder()
X{
X     register int   i, j;
X
X     for (j=0 ; j<N ; j++)
X          for (i=0 ; i<N ; i++) {
X               if (simp[i][j] < simp[l[j]][j])
X                    l[j] = i;
X               if (simp[i][j] > simp[h[j]][j])
X                    h[j] = i;
X          }
X}
E_O_F

echo extracting -- report.c
sed 's/^X//' > report.c << E_O_F
X#include "simplex.h"
X
Xreport()
X{
X     register int   i, j;
X     double         y, dy, sigma;
X
X     printf("\nProgram exited after %d iterations.\n\n", niter);
X
X     printf("The final simplex is:\n");
X     for (j=0 ; j<N ; j++) {
X          for (i=0 ; i<N ; i++) {
X               if ((i+1) % LW == 0)
X                    printf("\n");
X               printf("  %e", simp[j][i]);
X          }
X          printf("\n");
X     }
X     printf("\n\n");
X
X     printf("The mean is:");
X     for (i=0 ; i<N ; i++) {
X          if ((i+1) % LW == 0)
X               printf("\n");
X          printf("  %e", mean[i]);
X     }
X     printf("\n\n");
X
X     printf("The estimated fractional error is:");
X     for (i=0 ; i<N ; i++) {
X          if ((i+1) % LW == 0)
X               printf("\n");
X          printf("  %e", error[i]);
X     }
X     printf("\n\n");
X
X     printf("   #         X              Y             Y''             DY\n");
X     sigma = 0.0;
X     for (i=0 ; i<np ; i++) {
X          y = f(mean, data[i]);
X          dy = data[i][1] - y;
X          sigma += (dy*dy);
X          printf("%4d  ", i);
X          printf("%13e  %13e  ", data[i][0], data[i][1]);
X          printf("%13e  %13e\n", y, dy);
X     }
X     printf("\n");
X     sigma = sqrt(sigma);
X     printf("The standard deviation is %e\n\n", sigma);
X     sigma /= sqrt((double) (np-M));
X     printf("The estimated error of the function is %e\n\n", sigma);
X}
E_O_F

echo extracting -- sum_residual.c 
sed 's/^X//' > sum_residual.c << E_O_F
X#include "simplex.h"
X
X#define sqr(x) ((x) * (x))
X
Xsum_residual(x)
Xvector    x;
X{
X     register int   i;
X
X     x[N-1] = 0.0;
X
X     for (i=0 ; i<np ; i++)
X          x[N-1] += sqr(f(x, data[i]) - data[i][1]);
X}
E_O_F

exit

---
Name  : Brad L. McKinley --- (503) 889-4321
Usenet: ihnp4!chinet!blm             US Mail: M D R Professional Software, Inc.
CIS   : 70015,1205                            915 SW 3rd Avenue
Source: BDY171                                Ontario, Oregon 97914
"God created Arrakis to test the faithful."