Jspice3
interp.c
Go to the documentation of this file.
1 /***************************************************************************
2 JSPICE3 adaptation of Spice3e2 - Copyright (c) Stephen R. Whiteley 1992
3 Copyright 1990 Regents of the University of California. All rights reserved.
4 Authors: 1985 Wayne A. Christopher
5  1992 Stephen R. Whiteley
6 ****************************************************************************/
7 
8 /*
9  * Polynomial interpolation code.
10  */
11 
12 #include "spice.h"
13 #include "ftedefs.h"
14 
15 /* Interpolate data from oscale to nscale. data is assumed to be olen long,
16  * ndata will be nlen long. Returns false if the scales are too strange
17  * to deal with. Note that we are guaranteed that either both scales are
18  * strictly increasing or both are strictly decreasing.
19  */
20 
21 
22 #ifdef __STDC__
23 static int putinterval(double*,int,double*,int,double*,int,double,int);
24 static void printmat(char*,double*,int,int);
25 #else
26 static int putinterval();
27 static void printmat();
28 #endif
29 
30 
31 bool
32 ft_interpolate(data, ndata, oscale, olen, nscale, nlen, degree)
33 
34 double *data, *ndata, *oscale, *nscale;
35 int olen, nlen, degree;
36 {
37  double *result, *scratch, *xdata, *ydata;
38  int sign, lastone, i, l;
39 
40  if ((olen < 2) || (nlen < 2)) {
41  fprintf(cp_err, "Error: lengths too small to interpolate.\n");
42  return (false);
43  }
44  if ((degree < 1) || (degree > olen)) {
45  fprintf(cp_err, "Error: degree is %d, can't interpolate.\n",
46  degree);
47  return (false);
48  }
49 
50  if (oscale[1] < oscale[0])
51  sign = -1;
52  else
53  sign = 1;
54 
55  scratch = (double *) tmalloc((degree + 1) * (degree + 2) *
56  sizeof (double));
57  result = (double *) tmalloc((degree + 1) * sizeof (double));
58  xdata = (double *) tmalloc((degree + 1) * sizeof (double));
59  ydata = (double *) tmalloc((degree + 1) * sizeof (double));
60 
61  /* Deal with the first degree pieces. */
62  DCOPY(data, ydata, degree + 1);
63  DCOPY(oscale, xdata, degree + 1);
64 
65  while (!ft_polyfit(xdata, ydata, result, degree, scratch)) {
66  /* If it doesn't work this time, bump the interpolation
67  * degree down by one.
68  */
69 
70  if (--degree == 0) {
71  fprintf(cp_err, "ft_interpolate: Internal Error.\n");
72  return (false);
73  }
74  }
75 
76  /* Add this part of the curve. What we do is evaluate the polynomial
77  * at those points between the last one and the one that is greatest,
78  * without being greater than the leftmost old scale point, or least
79  * if the scale is decreasing at the end of the interval we are looking
80  * at.
81  */
82  lastone = -1;
83  for (i = 0; i < degree; i++) {
84  lastone = putinterval(result, degree, ndata, lastone,
85  nscale, nlen, xdata[i], sign);
86  }
87 
88  /* Now plot the rest, piece by piece. l is the
89  * last element under consideration.
90  */
91  for (l = degree + 1; l < olen; l++) {
92 
93  /* Shift the old stuff by one and get another value. */
94  for (i = 0; i < degree; i++) {
95  xdata[i] = xdata[i + 1];
96  ydata[i] = ydata[i + 1];
97  }
98  ydata[i] = data[l];
99  xdata[i] = oscale[l];
100 
101  while (!ft_polyfit(xdata, ydata, result, degree, scratch)) {
102  if (--degree == 0) {
103  fprintf(cp_err,
104  "interpolate: Internal Error.\n");
105  return (false);
106  }
107  }
108  lastone = putinterval(result, degree, ndata, lastone,
109  nscale, nlen, xdata[i], sign);
110  }
111  txfree((char*)scratch);
112  txfree((char*)xdata);
113  txfree((char*)ydata);
114  txfree((char*)result);
115  return (true);
116 }
117 
118 
119 /* Takes n = (degree+1) doubles, and fills in result with the n coefficients
120  * of the polynomial that will fit them. It also takes a pointer to an
121  * array of n ^ 2 + n doubles to use for scratch -- we want to make this
122  * fast and avoid doing mallocs for each call.
123  */
124 
125 
126 bool
127 ft_polyfit(xdata, ydata, result, degree, scratch)
128 
129 double *xdata, *ydata, *result, *scratch;
130 int degree;
131 {
132  double *mat1 = scratch;
133  int l, k, j, i;
134  int n = degree + 1;
135  double *mat2 = scratch + n * n;
136  double d;
137 
138 /*
139 fprintf(cp_err, "n = %d, xdata = ( ", n);
140  for (i = 0; i < n; i++)
141  fprintf(cp_err, "%G ", xdata[i]);
142  fprintf(cp_err, ")\n");
143  fprintf(cp_err, "ydata = ( ");
144  for (i = 0; i < n; i++)
145  fprintf(cp_err, "%G ", ydata[i]);
146  fprintf(cp_err, ")\n");
147 */
148 
149  bzero((char *) result, n * sizeof(double));
150  bzero((char *) mat1, n * n * sizeof (double));
151  DCOPY(ydata, mat2, n);
152 
153  /* Fill in the matrix with x^k for 0 <= k <= degree for each point */
154  l = 0;
155  for (i = 0; i < n; i++) {
156  d = 1.0;
157  for (j = 0; j < n; j++) {
158  mat1[l] = d;
159  d *= xdata[i];
160  l += 1;
161  }
162  }
163 
164  /* Do Gauss-Jordan elimination on mat1. */
165  for (i = 0; i < n; i++) {
166  int lindex;
167  double largest;
168 
169  /* choose largest pivot */
170  for (j=i, largest = mat1[i * n + i], lindex = i; j < n; j++) {
171  if (fabs(mat1[j * n + i]) > largest) {
172  largest = fabs(mat1[j * n + i]);
173  lindex = j;
174  }
175  }
176  if (lindex != i) {
177  /* swap rows i and lindex */
178  for (k = 0; k < n; k++) {
179  d = mat1[i * n + k];
180  mat1[i * n + k] = mat1[lindex * n + k];
181  mat1[lindex * n + k] = d;
182  }
183  d = mat2[i];
184  mat2[i] = mat2[lindex];
185  mat2[lindex] = d;
186  }
187 #ifdef notdef
188  if (mat1[i * n + i] == 0.0)
189  for (j = i; j < n; j++)
190  if (mat1[j * n + i] != 0.0) {
191  /* Swap rows i and j. */
192  for (k = 0; k < n; k++) {
193  d = mat1[i * n + k];
194  mat1[i * n + k] =
195  mat1[j * n + k];
196  mat1[j * n + k] = d;
197  }
198  d = mat2[i];
199  mat2[i] = mat2[j];
200  mat2[j] = d;
201  break;
202  }
203 #endif
204  /* Make sure we have a non-zero pivot. */
205  if (mat1[i * n + i] == 0.0) {
206  /* this should be rotated. */
207  return (false);
208  }
209  for (j = i + 1; j < n; j++) {
210  d = mat1[j * n + i] / mat1[i * n + i];
211  for (k = 0; k < n; k++)
212  mat1[j * n + k] -= d * mat1[i * n + k];
213  mat2[j] -= d * mat2[i];
214  }
215  }
216 
217  for (i = n - 1; i > 0; i--)
218  for (j = i - 1; j >= 0; j--) {
219  d = mat1[j * n + i] / mat1[i * n + i];
220  for (k = 0; k < n; k++)
221  mat1[j * n + k] -=
222  d * mat1[i * n + k];
223  mat2[j] -= d * mat2[i];
224  }
225 
226  /* Now write the stuff into the result vector. */
227  for (i = 0; i < n; i++) {
228  result[i] = mat2[i] / mat1[i * n + i];
229  /* printf(cp_err, "result[%d] = %G\n", i, result[i]);*/
230  }
231 
232 #define ABS_TOL 0.001
233 #define REL_TOL 0.001
234 
235  /* Let's check and make sure the coefficients are ok. If they aren't,
236  * just return false. This is not the best way to do it.
237  */
238  for (i = 0; i < n; i++) {
239  d = ft_peval(xdata[i], result, degree);
240  if (fabs(d - ydata[i]) > ABS_TOL) {
241  /*
242  fprintf(cp_err,
243  "Error: polyfit: x = %le, y = %le, int = %le\n",
244  xdata[i], ydata[i], d);
245  printmat("mat1", mat1, n, n);
246  printmat("mat2", mat2, n, 1);
247  */
248  return (false);
249  }
250  else if (fabs(d - ydata[i]) / (fabs(d) > ABS_TOL ? fabs(d) :
251  ABS_TOL) > REL_TOL) {
252  /*
253  fprintf(cp_err,
254  "Error: polyfit: x = %le, y = %le, int = %le\n",
255  xdata[i], ydata[i], d);
256  printmat("mat1", mat1, n, n);
257  printmat("mat2", mat2, n, 1);
258  */
259  return (false);
260  }
261  }
262 
263  return (true);
264 }
265 
266 
267 /* Returns the index of the last element that was calculated. oval is the
268  * value of the old scale at the end of the interval that is being interpolated
269  * from, and sign is 1 if the old scale was increasing, and -1 if it was
270  * decreasing.
271  */
272 
273 static int
274 putinterval(poly, degree, nvec, last, nscale, nlen, oval, sign)
275 
276 double *poly, *nvec, *nscale, oval;
277 int degree, last, nlen, sign;
278 {
279  int end, i;
280 
281  /* See how far we have to go. */
282  for (end = last + 1; end < nlen; end++)
283  if (nscale[end] * sign > oval * sign)
284  break;
285  end--;
286 
287  for (i = last + 1; i <= end; i++)
288  nvec[i] = ft_peval(nscale[i], poly, degree);
289  return (end);
290 }
291 
292 
293 static void
294 printmat(name, mat, m, n)
295 
296 char *name;
297 double *mat;
298 int m, n;
299 {
300  int i, j;
301 
302  printf("\n\r=== Matrix: %s ===\n\r", name);
303  for (i = 0; i < m; i++) {
304  printf(" | ");
305  for (j = 0; j < n; j++)
306  printf("%G ", mat[i * n + j]);
307  printf("|\n\r");
308  }
309  printf("===\n\r");
310  return;
311 }
312 
313 
314 double
315 ft_peval(x, coeffs, degree)
316 
317 double x, *coeffs;
318 int degree;
319 {
320  double y;
321  int i;
322 
323  if (!coeffs)
324  return (0.0); /* Should not happen */
325 
326  y = coeffs[degree]; /* there are (degree+1) coeffs */
327  for (i = degree - 1; i >= 0; i--) {
328  y *= x;
329  y += coeffs[i];
330  }
331  return (y);
332 }
Definition: cddefs.h:312
int bzero(char *ptr, int num)
Definition: string.c:357
FILE * m
Definition: proc2mod.c:47
#define DCOPY(s, d, n)
Definition: ftedefs.h:55
static int putinterval()
bool ft_interpolate(double *data, double *ndata, double *oscale, int olen, double *nscale, int nlen, int degree)
Definition: interp.c:32
FILE * cp_err
Definition: help.c:101
bool ft_polyfit(double *xdata, double *ydata, double *result, int degree, double *scratch)
Definition: interp.c:127
char * tmalloc()
#define ABS_TOL
Definition: cddefs.h:237
void txfree()
#define REL_TOL
static void printmat()
double ft_peval(double x, double *coeffs, int degree)
Definition: interp.c:315
Definition: cddefs.h:109