SDDS ToolKit Programs and Libraries for C and Python
Loading...
Searching...
No Matches
lsfn.c
Go to the documentation of this file.
1/**
2 * @file lsfn.c
3 * @brief Computes nth order polynomial least squares fit.
4 *
5 * This file contains the implementation of the `lsfn` function,
6 * which computes a polynomial least squares fit of a specified order
7 * to given data points. It supports both weighted and unweighted fitting.
8 *
9 * Author: Michael Borland, 1986.
10 */
11
12#include "matlib.h"
13#include "mdb.h"
14
15int p_merror(char *message);
16
17/**
18 * @brief Computes nth order polynomial least squares fit.
19 *
20 * This function performs an nth order polynomial least squares fit to the provided data.
21 * It supports both weighted and unweighted fitting based on the standard deviations provided.
22 *
23 * @param xd Array of x data points.
24 * @param yd Array of y data points.
25 * @param sy Array of standard deviations of y data points. If NULL or all elements are equal, an unweighted fit is performed.
26 * @param nd Number of data points.
27 * @param nf Order of the polynomial fit (degree of the polynomial).
28 * @param coef Array to store the computed coefficients of the polynomial.
29 * @param s_coef Array to store the standard deviations of the coefficients.
30 * @param chi Pointer to store the reduced chi-squared value. If NULL, chi-squared is not computed.
31 * @param diff Array to store the differences between fitted and actual y values. If NULL, differences are not stored.
32 * @return Returns 1 on success, or 0 on error.
33 */
34long lsfn(double *xd, double *yd, double *sy, /* data */
35 long nd, /* number of data points */
36 long nf, /* y = a_0 + a_1*x ... a_nf*x^nf */
37 double *coef, /* place to put co-efficients */
38 double *s_coef, /* and their sigmas */
39 double *chi, /* place to put reduced chi-squared */
40 double *diff /* place to put difference table */
41) {
42 long i, j, nt, unweighted;
43 double xp, *x_i, x0;
44 MATRIX *X = NULL, *Y = NULL, *Yp = NULL, *C = NULL, *C_1 = NULL, *Xt = NULL, *A = NULL, *Ca = NULL, *XtC = NULL, *XtCX = NULL, *T = NULL, *Tt = NULL, *TC = NULL;
45 long status = 0;
46
47 nt = nf + 1;
48 if (nd < nt) {
49 printf("error: insufficient data for requested order of fit\n");
50 printf("(%ld data points, %ld terms in fit)\n", nd, nt);
51 exit(1);
52 }
53
54 unweighted = 1;
55 if (sy)
56 for (i = 1; i < nd; i++)
57 if (sy[i] != sy[0]) {
58 unweighted = 0;
59 break;
60 }
61
62 /* allocate matrices */
63 m_alloc(&X, nd, nt);
64 m_alloc(&Y, nd, 1);
65 m_alloc(&Yp, nd, 1);
66 m_alloc(&Xt, nt, nd);
67 if (!unweighted) {
68 m_alloc(&C, nd, nd);
69 m_alloc(&C_1, nd, nd);
70 m_zero(C);
71 m_zero(C_1);
72 }
73 m_alloc(&A, nt, 1);
74 m_alloc(&Ca, nt, nt);
75 m_alloc(&XtC, nt, nd);
76 m_alloc(&XtCX, nt, nt);
77 m_alloc(&T, nt, nd);
78 m_alloc(&Tt, nd, nt);
79 m_alloc(&TC, nt, nd);
80
81 /* Compute X, Y, C, C_1. X[i][j] = (xd[i])^j. Y[i][0] = yd[i].
82 * C = delta(i,j)*sy[i]^2 (covariance matrix of yd)
83 * C_1 = INV(C)
84 */
85 for (i = 0; i < nd; i++) {
86 x_i = X->a[i];
87 x0 = xd[i];
88 xp = 1.0;
89 Y->a[i][0] = yd[i];
90 if (!unweighted) {
91 C->a[i][i] = sqr(sy[i]);
92 C_1->a[i][i] = 1 / C->a[i][i];
93 }
94 for (j = 0; j < nt; j++) {
95 x_i[j] = xp;
96 xp *= x0;
97 }
98 }
99
100 /* Compute A, the matrix of coefficients.
101 * Weighted least-squares solution is A = INV(Xt.INV(C).X).Xt.INV(C).y
102 * Unweighted solution is A = INV(Xt.X).Xt.y
103 */
104 if (unweighted) {
105 /* eliminating 2 matrix operations makes this much faster than a weighted fit
106 * if there are many data points.
107 */
108 if (!m_trans(Xt, X))
109 { status = p_merror("transposing X"); goto cleanup; }
110 if (!m_mult(XtCX, Xt, X))
111 { status = p_merror("multiplying Xt.X"); goto cleanup; }
112 if (!m_invert(XtCX, XtCX))
113 { status = p_merror("inverting XtCX"); goto cleanup; }
114 if (!m_mult(T, XtCX, Xt))
115 { status = p_merror("multiplying XtX.Xt"); goto cleanup; }
116 if (!m_mult(A, T, Y))
117 { status = p_merror("multiplying T.Y"); goto cleanup; }
118
119 /* Compute covariance matrix of A, Ca = (T.Tt)*C[0][0] */
120 if (!m_trans(Tt, T))
121 { status = p_merror("computing transpose of T"); goto cleanup; }
122 if (!m_mult(Ca, T, Tt))
123 { status = p_merror("multiplying T.Tt"); goto cleanup; }
124 if (!m_scmul(Ca, Ca, sy ? sqr(sy[0]) : 1))
125 { status = p_merror("multiplying T.Tt by scalar"); goto cleanup; }
126 } else {
127 if (!m_trans(Xt, X))
128 { status = p_merror("transposing X"); goto cleanup; }
129 if (!m_mult(XtC, Xt, C_1))
130 { status = p_merror("multiplying Xt.C_1"); goto cleanup; }
131 if (!m_mult(XtCX, XtC, X))
132 { status = p_merror("multiplying XtC.X"); goto cleanup; }
133 if (!m_invert(XtCX, XtCX))
134 { status = p_merror("inverting XtCX"); goto cleanup; }
135 if (!m_mult(T, XtCX, XtC))
136 { status = p_merror("multiplying XtCX.XtC"); goto cleanup; }
137 if (!m_mult(A, T, Y))
138 { status = p_merror("multiplying T.Y"); goto cleanup; }
139
140 /* Compute covariance matrix of A, Ca = T.C.Tt */
141 if (!m_mult(TC, T, C))
142 { status = p_merror("multiplying T.C"); goto cleanup; }
143 if (!m_trans(Tt, T))
144 { status = p_merror("computing transpose of T"); goto cleanup; }
145 if (!m_mult(Ca, TC, Tt))
146 { status = p_merror("multiplying TC.Tt"); goto cleanup; }
147 }
148
149 for (i = 0; i < nt; i++) {
150 coef[i] = A->a[i][0];
151 if (s_coef)
152 s_coef[i] = sqrt(Ca->a[i][i]);
153 }
154
155 /* Compute Yp = X.A, use to compute chi-squared */
156 if (chi) {
157 if (!m_mult(Yp, X, A))
158 { status = p_merror("multiplying X.A"); goto cleanup; }
159 *chi = 0;
160 for (i = 0; i < nd; i++) {
161 xp = (Yp->a[i][0] - yd[i]);
162 if (diff != NULL)
163 diff[i] = xp;
164 xp /= sy ? sy[i] : 1;
165 *chi += xp * xp;
166 }
167 if (nd != nt)
168 *chi /= (nd - nt);
169 }
170
171 status = 1;
172
173cleanup:
174 m_free(&X);
175 m_free(&Y);
176 m_free(&Yp);
177 m_free(&Xt);
178 if (!unweighted) {
179 m_free(&C);
180 m_free(&C_1);
181 }
182 m_free(&A);
183 m_free(&Ca);
184 m_free(&XtC);
185 m_free(&XtCX);
186 m_free(&T);
187 m_free(&Tt);
188 m_free(&TC);
189 return (status);
190}
long lsfn(double *xd, double *yd, double *sy, long nd, long nf, double *coef, double *s_coef, double *chi, double *diff)
Computes nth order polynomial least squares fit.
Definition lsfn.c:34