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